위의 링크에서 스프링 프로젝트 생성하고 build.gradle를 open하면 프로젝트가 생성된다.
스프링 부트에는 tomcat가 내장되어 있어 별도로 설치할 필요가 없다.
위 링크에서 spring에 대한 자료들을 찾아볼 수 있다.
참고자료 (인프런-스프링 입문 강의자료)
@ResponseBody를 사용하면 return에서 templates/하위의 html 파일을 찾는 것이 아닌
return 값 그대로 뿌려진다.
@ResponseBody는 기본이 json방식이다. {key : value}
getter, setter은 프로퍼티 접근 방식이라고도 한다.
실무에서는 아래와 같이 map를 선언할때 동시성 문제로 인해
HashMap<>()가 아닌 ConcurrentHashMap<>()을 사용한다.
private static Map<Long, Member> store = new HashMap<>();
private static Map<Long, Member> store = new ConcurrentHashMap<>()
System.out.println(); 으로 매번 값을 비교하여 출력하는 것 대신
Assertions를 사용한다.
import org.junit.jupiter.api.Assertions;
import org.assertj.core.api.Assertions;
이렇게 두 종류가 있는데
첫번째는 Assertions.assertEquals(member, result); 방식으로
앞의 member 인자가 기대값, 뒤의 인자가 실제 값이다.
두번째로 Assertions.assertThat(member).isEqualTo(result); 방식은
일반 문법과 같이 member을 result와 비교하는 것이다.
구조
Repository 인터페이스를 생성해서 저장 조회등의 메서드를 선언해주고
Repository 인터페이스의 구현부를 작성해서
Service에서 비지니스 로직을 수행한다.
메서드의 네이밍은 Service에서는 비지니스 관점에서 의존적으로 설계하고,
Repository는 데이터가 들어가는 장소이기 때문에 기계적인 네이밍을 사용.
- 가장 메인이되는 HelloSpringApplication.java에는 @SpringBootApplication가 선언되어있는데,
이 어노테이션 인터페이스를 보면 @ComponentScan이 선언되어 있다.
이는 즉, 스프링이 구동될때 @Component를 찾아서 빈을 등록해주는 것이다.
@Controller, @Service, @Repository 모두 @Component를 포함하고 있어서 순서대로 찾는다.
- 스프링 빈을 등록해줄 .java파일을 생성하고, 클래스 위에 @Configuration 어노테이션을 작성,
그 후 @Bean 어노테이션을 선언해서 메서드를 생성해주면 된다.
단, Controller.java에서는 @Controller 어노테이션이 있어야 한다.
사용 예제)
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
@Configuration 사용의 최대의 장점은 jdbc 연결 방식이 바뀔때 이쪽 연결만 바꿔주면 된다.
db연결 방식이 바뀐다 해서 각각의 Service들의 소스를 바꿔줄 필요가 없음
private DataSource dataSource;
@Autowired
public SpringConfig(DataSource dataSource) {
this.dataSource = dataSource;
}
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
//return new MemoryMemberRepository();
return new JdbcMemberRepository(dataSource);
}
// 필드 주입. 단점) 값을 중간에 바꿀수가 없다, 순환 참조로 인해 StackOverflowError발생
@Autowired
private MemberService memberService;
// setter 주입. 단점) public라 아무나 호출할 수 있게 된다.
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService;
}
// 생성자 주입(가장 선호). 초반에 스프링 구동시 조립되고 끝.
// 의존관계는 실행중에 동적으로 변하는 경우가 거의 없으므로 생성자 주입 권장
// final로 인해 Controller 내부에서 memberService의 객체를 바꿔치기 못함
// 생성자에 들어가는 파라미터가 하나일 경우 @Autowired를 없애도 된다.
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
추가로 생성자 주입에서의 private final MemberService memberService;에서 final은 변하지 않는 상수 값을 입력 해줘야 함으로 생성자를 주입해줌.
한클래스 안에 생성자가 1개일 경우 @Autowired를 생략해도 된다.
생성자 주입을 해야하는 상세한 이유
yaboong.github.io/spring/2019/08/29/why-field-injection-is-bad/
JPA = ORM(객체와 관계형테이블을 매핑한다는 의미)
JPA를 사용하려면 EntityManager을 주입해야 한다.
기존 jdbc -> jpa -> springdata-jpa
인터페이스를 통한 기본적인 CRUD를 제공한다.
예를들어) findByName(), findByEmail() 처럼 메서드 이름 만으로 조회 가능.
페이징 기능도 제공
JpaRepository를 상속 받으면 @Repository 어노테이션을 쓰지않는다.
why? JpaRepository를 따라가면 최상위에 @Repository가 이미 선언되어 있기 때문!
스프링 데이터 JPA가 'SpringDataJpaMemberRepository"를 스프링 빈으로 자동 등록한다.
관점지향 프로그래밍
간단한 활용을 예로 들면
메서드별 실행 시간을 알고 싶다라고 했을때,
public Long join(Member member) {
long start = System.currentTimeMillis();
try{
validateDuplicateMember(member); // 중복 회원 검증
memberRepository.save(member);
return member.getId();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish-start;
System.out.println("join = " + timeMs + "ms");
}
}
이런식으로 메서드마다 소스를 추가해야 하는 상황이 발생했을때 AOP를 이용할 수 있다.
TimeTraceAop.java라는 파일을 생성
// spring bean에 등록을 해줘야 한다.
// @Component를 사용하면 ComponentScan이 찾으므로 사용해도 되지만
// 되도록 Spring Bean에 직접등록 하는게 좋다.
@Aspect
@Component
public class TimeTraceAop {
@Around("execution(* hello.hellospring..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("START: " + joinPoint.toString());
try {
Object result = joinPoint.proceed();
return result;
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END: "+joinPoint.toString() + " " + timeMs + "ms");
}
}
}
위의 aop추가 후 구동한 결과
@Around로 지정되어 있는 부분은 실제 메서드가아닌 프록시 메서드가 생성이 되고 프록시를 먼저 거쳐서
실제 메서드가 실행이된다. 아래 그림 참고
어노테이션 (0) | 2021.04.16 |
---|---|
Intellij 단축키 (0) | 2021.04.06 |
도메인 관련 (0) | 2021.04.01 |
ORM(JPA), SQL Mapper(Mybatis), JDBC (0) | 2021.03.18 |
블록체인(BlockChain) (0) | 2021.03.14 |