📌 @Configuration과 싱글톤
📌 @Configuration과 바이트코드 조작의 마법
appConfig 코드를 보면 new MemoryMemberRepository()
가 여러번 실행되는 것을 볼 수 있다.
🤔 과연 싱글톤 패턴일까?
@Bean memberService -> memberRepository() -> new MemoryMemberRepository()
@Bean orderService() -> memberRepository() -> new MemoryMemberRepository()
discountPolicy() -> new RateDiscountPolicy();
확인을 위한 메소드 추가
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
//테스트 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
//테스트 용도
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
테스트 코드
public class ConfigurationSingletonTest {
@Test
void configrationTeset() {
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);
assertThat(memberService.getMemberRepository()).isSameAs(memberRepository);
assertThat(orderService.getMemberRepository()).isSameAs(memberRepository);
}
}
💻 실행화면
memberService -> memberRepository = hello.core.member.MemoryMemberRepository@b4c8dbea
orderService -> memberRepository = hello.core.member.MemoryMemberRepository@b4c8dbea
memberRepository = hello.core.member.MemoryMemberRepository@b4c8dbea
👉모두 같은 Repository를 반환한다는 것을 볼 수 있다.
위 코드에서 memberService는 총 세번 호출되어야할 것이다.
그러나 기록을 보면 그렇지 않다.
❣️으엥? : 기록을 살펴보자
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
memberService -> memberRepository = hello.core.member.MemoryMemberRepository@206a2c64
orderService -> memberRepository = hello.core.member.MemoryMemberRepository@206a2c64
memberRepository = hello.core.member.MemoryMemberRepository@206a2c64
➡️ memberService
가 딱 한번만 호출된다. 왜 그런걸까?
자바 코드 자체가 세번 호출 되어야하는데 그렇지 않다. 어떤 조작이 있는 것일까?
스프링 컨테이너는 싱글톤 레지스트리이다. 따라서 스프링 빈이 싱글톤이 되도록 보장해주어야한다. 어떻게 하면 되는걸까?
@Test
void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}
🤔 당연히 실행결과는 AppConfig
겠지?
💻 실행화면
bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$e2f5472c
😲 어라라? 아니네?
AppConfig@CGLIB 예상 코드
@Bean
public MemberRepository memberRepository() {
if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
return 스프링 컨테이너에서 찾아서 반환;
} else { //스프링 컨테이너에 없으면
기존 로직을 호출해서 MemoryMemberRepository를 생성하고 스프링 컨테이너에 등록
return 반환
}
➡️ 스프링 빈에 등록이 되어있으면, 스프링 컨테이너에서 찾아서 반환하기 때문에,위 코드에서 memberService이 한번만 호출이 되는 것이다.
(우리는 실제로 오버라이딩된 AppConfig@@@를 호출!!)
✔️ 덕분에 싱글톤이 보장되는 것이다.
@Configuration
을 안붙이면?org.springframework.beans.factory.NoSuchBeanDefinitionException:
No bean named 'memberRepository' available
ctrl + shift + r