//@Bean memberService -> new MemoryMemberrepository() 를 호출한다. 객체가 생성된다.
//@Bean orderService -> new OrderServiceImpl()을 호출 -> new MemoryMemberRepository()를 또 호출한다.
결과적으로 각각 다른 2개의 MemoryMemberRepository가 생성되면서 싱글톤이 깨지는 것이 아닌가?
MemberServiceImpl
package hello.core.Member;
public class MemberServiceImpl implements MemberService{
...
//테스트 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
OrderServiceImpl
package hello.core.order;
import ...
public class OrderServiceImpl implements OrderService{
...
//테스트 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
Test
package hello.core.singleton;
import ...
public class ConfigurationSingletonTest {
@Test
void configurationTest() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository = ac.getBean("memberRepository", MemberRepository.class);
MemberRepository memberRepository1 = memberService.getMemberRepository();
MemberRepository memberRepository2 = orderService.getMemberRepository();
System.out.println("memberService -> memberRepository = " + memberRepository1);
System.out.println("orderService -> memberRepository = " + memberRepository2);
System.out.println("memberRepository -> memberRepository = " + memberRepository);
assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}
}
실행
memberService -> memberRepository = hello.core.Member.MemoryMemberRepository@7d9e8ef7
orderService -> memberRepository = hello.core.Member.MemoryMemberRepository@7d9e8ef7
memberRepository -> memberRepository = hello.core.Member.MemoryMemberRepository@7d9e8ef7
add on-demand static import => alt Enter
확인해보면 memberRepository 인스턴스는 모두 같은 인스턴스가 공유되어 사용된다.
혹시 두번 호출이 안되는 것일까?
Appconfig
package hello.core;
import ...
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
System.out.println("call AppConfig.memberService"); //soutm
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());
}
...
}
System.out.println("AppConfig.memberService"); //soutm
실행
...
call AppConfig.memberService
21:18:51.765 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberRepository'
call AppConfig.memberRepository
21:18:51.767 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'
call AppConfig.orderService
...
그러나 출력결과는 모두 1번만 호출된다.
package hello.core.singleton;
import ...
public class ConfigurationSingletonTest {
@Test
void configurationTest() {
...
@Test
void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}
}
실행
...
bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$5218c930
순수한 클래스라면 'class hello.core.AppConfig' 와 같이 출력되야 한다.
이것은 내가 만든 클래스가 아니라 스프링이 CGLIB라는 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다!
그 임의의 다른 클래스가 싱글톤이 보장되도록 해준다. 아마도 다음과 같이 바이트 코드를 조작해서 작성되어 있을 것이다.
(실제 CGLIB의 내부 기술은 매우 복잡하다.
@Bean
public MemberRepository memberRepository() {
if(memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
return 스프링 컨테이너에서 찾아서 반환;
}else { //스프링 컨테이너에 없으면
기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
return 반환
}
}
...
//@Configuration
public class AppConfig {
Test
...
call AppConfig.memberService
call AppConfig.memberRepository
...
call AppConfig.memberRepository
...
call AppConfig.orderService
...
call AppConfig.memberRepository
...
bean = class hello.core.AppConfig
configurationTest()
memberService -> memberRepository = hello.core.Member.MemoryMemberRepository@2bdd8394
orderService -> memberRepository = hello.core.Member.MemoryMemberRepository@5f9edf14
memberRepository -> memberRepository = hello.core.Member.MemoryMemberRepository@68746f22
이 출력결과를 통해서 MemberRepository가 총 3번 호출된 것을 알 수 있다.