개요 : AppConfig 에 등록된 Bean 호출을 확인해보자
@Configuration
public class AppConfig {
// 생성자 주입 ! -> 이걸 통해서 DIP 를 지킬 수 있음 구현객체를 이놈이 생성하는거지
@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());
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
예상 시나리오
Bean 이 등록될 때 호출 된다. @Bean 붙어 있는 녀석들은 각각 모두 호출되니까
아래 처럼 호출 되어야한다.
@Bean memberService→new MemoryMemberRepository()
call AppConfig.memberService
call AppConfig.memberRepository
@Bean memberRepository
call AppConfig.memberRepository
@Bean orderService → new MemoryMemberRepository()
call AppConfig.orderService
call AppConfig.memberRepository
실제 시나리오
각 한번 씩만 호출된다. → 어째서 왜 Why ?!
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
예상 시나리오 처럼 안되는 이유
@Configuration 을 적용한 AppConfig 이기 때문이다.
AppConfig 의 클래스를 조회 해보면 class hello.core.AppConfig 이라고 나와야한다.
그런데 , class hello.core.AppConfig\$\$SpringCGLIB\$\$0 이렇게 나온다.
이 이유는 스프링이 CGLIB라는 바이트코드 조작 라이브러리를 사용해서
AppConfig 클래스를 상속받은 임의의 다른 클래스(즉, 다른AppConfig 클래스)를 만듦
이 다른 클래스를 스프링 빈 으로 등록한다.
그럼 스프링이 새로 만드는 (상속받아서 조작한) 클래스는 어떤 것이 다를까 ?
@Bean이 붙은 메서드들에 이미 스프링 빈이 존재하는지 확인한다.
존재한다면 → 존재하는 빈을 반환
존재하지않는다면 → 생성해서 스프링 빈으로 등록하고 반환
즉, @Configuration 에 의해서 스프링이 자체적으로 싱글톤이 보장되는 것이다.
이렇게 스프링에 의해서 싱글톤을 보장하게되면 기존 싱글톤에 의한 단점들을 해결할 수 있게된다.
- 싱글톤에 의한 단점
- 싱글톤 패턴을 구현하는 코드 자체가 많이 들어간다.
- 의존관계상 클라이언트가 구체 클래스에 의존한다.→ DIP를 위반한다.
- 클라이언트가 구체 클래스에 의존해서 OCP 원칙을 위반할 가능성이 높다.
- 테스트하기 어렵다.
- 내부 속성을 변경하거나 초기화 하기 어렵다.
- private 생성자로 자식 클래스를 만들기 어렵다 즉, 유연성이 떨어진다.
reference