스프링 핵심원리 기본편14 - @Configuration과 싱글톤/@Configuration과 바이트코드 조작의 마법
안녕하세요 오늘도 공부한 내용을 정리해 보도록 하겠습니다.
먼저 @Configuration을 공부해 보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService oderService() {
return new OrderServiceImpl(memberRepository(),discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
|
cs |
위소스에서 meberService를 호출하면 memberRepository가 호출되고 OrderService를 호출하여도 memberRepository가 호출이 됩니다. 이러면 같은 게 2번 호출되어 싱글톤이 깨질 거 같지만 깨지지 않습니다.
테스트를 위해 MemberServiceImpl 과 OrderServiceImpl에 아래의 소스를 추가시켜 줍니다.
1
2
3
|
public MemberRepository getMemberRepository() {
return memberRepository;
}
|
cs |
구체 타임으로 꺼내는 것은 좋은 방법은 아니지만 테스트 만을 위한 소스 여서 위와 같은 형태로 만들어 주었습니다.
그리고 test 소스를 아래와 같이 만들어 주고
1
2
3
4
5
6
7
8
9
10
11
|
@Test
void configurationTest() {
AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService",MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
System.out.println("memberService -> memberRepository" + memberRepository1);
System.out.println("orderService -> memberRepository" + memberRepository2);
}
|
cs |
수행해 주면 두 개가 같은 것을 볼 수 있습니다.
분명 memberRepository가 2번 호출되는데 같은 것이 나오는 이유는 소스를 통해 알아보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
@Bean
public MemberService memberService() {
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(),discountPolicy());
}
|
cs |
위처럼 호출될때마다 print를 해주어 확인을 해보면 5번이 호출되어햐 할 거 같지만 3번만 호출이 됩니다.
위처럼 스프링은 싱글톤을 보장해 줍니다.
스프링 컨테이너는 싱글톤 레이지스트리입니다. 그래서 싱글톤이 보장되도록 해주어야 합니다.
먼저 @Configuration이 어찌 동작하는 건지 확인하기 위해 아래와 같은 코드로 먼저 확인을 해보겠습니다.
1
2
3
4
5
6
7
|
@Test
void configurationDeep() {
AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}
|
cs |
위 코드의 결과는 bean = class hello.core.AppConfig$$SpringCGLIB$$ 이런 이상한 값이 나오게 되는데
순수한 클래스라면 AppConfig까지만 나와야 하는데 지금은 CGLIB가 붙고 이것은 바이트 조작 라이브러리를 사용하여 AppConfig를 통해 상속받은 임의의 다른 클래스를 만들고 그 다른 클래스를 빈으로 등록한 것입니다. 저 임의의 다른 클래스가 싱글톤을 보장해 줍니다.
CGLIB는 기존에 스프링 컨테이너에 등록되어 있는지 확인해 보고 등록되어 있다면 스프링 컨테이너에서 찾아 반환한고 아니라면 기존 로직을 호출 스프링 컨테이너에 등록해 줍니다.* CGLIB는 AppConfig의 자식 타입으로 AppConfig로 조회가 가능합니다.
@Configuration을 안붙이고 @Bean만 사용해도 바이트코드 조작 없이 @Bean이 등록되지만 싱글톤은 깨지게 됩니다.@Configuration을 주석처리하고 돌려주게 되면 순수한 클래스에서 나오는 값인 bean = class hello.core.AppConfig가 나오게 됩니다.
오늘 공부는 요기서 마치도록 하겠습니다.