@Configuration은 사실 싱글톤을 위해 존재하는 것이다.
그런데 이상한 점이 있다. 이전에 작성했던 AppConfig 코드를 다시 확인해보자.
@Configuration
public class AppConfig {
@Bean
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
//return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
memberService 빈을 만드는 코드를 보면 memberRepository()를 호출하는데, 이 메서드는 new MemoryMemberRepository() 객체를 생성한다. 동일하게 OrderService 빈을 만드는 코드 또한 memberRepository()를 호출하는데, 이 메서드도 new MemoryMemberRepository() 객체를 생성한다.
결과적으로 각각 다른 2개의 MemoryMemberRepository가 생성되면서 싱글톤이 깨지는 것처럼 보인다. 스프링 컨테이너는 이 문제를 어떻게 해결할까?
@Configuration과 바이트 조작
스프링 컨테이너는 싱글톤 레지스트리이다. 따라서 스프링 빈이 싱글톤이 되도록 보장해주어야 한다.
그래서 스프링은 클래스의 바이트코드를 조작하는 라이브러리를 사용한다.
다음 코드를 보자.
@Test
void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//AppConfig도 스프링 빈으로 등록된다.
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
//출력: bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70
}
AnnotationConfigApplicationContext에 파라미터로 넘긴 값은 스프링 빈으로 등록된다. 그래서 AppConfig도 스프링 빈이 된다. AppConfig 스프링 빈을 조회해서 클래스 정보를 출력해보자.
이와 같이 출력되는 것을 확인할 수 있다.
만약 순수한 클래스라면 다음과 같이 출력되어야 한다.
그런데 예상과 달리 뒤에 xxxxCGLIB가 붙으면서 상당히 복잡해진 것을 확인할 수 있다.
이것은 내가 만든 클래스가 아니라 스프링이 CGLIB라는 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다.
말이 좀 복잡한데 이해하기 쉽게 그림으로 확인해보자.
이 임의의 다른 클래스가 바로 싱글톤이 보장되도록 해준다.
@Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다. 덕분에 싱글톤이 보장되는 것이다.
참고 : AppConfig@CGLIB는 AppConfig의 자식 타입이므로, AppConfig 타입으로 조회할 수 있다.
그럼, @Configuration을 적용하지 않고, @Bean만 적용하면 어떻게 될까?
이렇게 사용하면 AppConfig가 CGLIB 기술 없이 순수한 AppConfig로 스프링 빈에 등록된다. 하지만, 싱글톤을 보장하진 않는다.
따라서, 고민할 필요없이 스프링 설정 정보는 항상 @Configuration을 사용하자!
'스프링(Spring) > Spring' 카테고리의 다른 글
[Spring] 탐색 위치와 기본 스캔 대상 (0) | 2022.11.23 |
---|---|
[Spring] 컴포넌트 스캔과 의존관계 자동 주입 (0) | 2022.11.23 |
[Spring] 싱글톤 방식의 주의점 (0) | 2022.11.21 |
[Spring] 싱글톤 컨테이너 (0) | 2022.11.21 |
[Spring] 싱글톤 패턴 (0) | 2022.08.10 |