스프링 컨테이너는 다들 알다싶이, 싱글톤 패턴을 적용시켜 빈 객체를 하나로 관리하여 메모리를 효율적으로 관리한다. 싱글톤 패턴은 메모리를 최적으로 유지할 수 있다는 장점이 있지만, 싱글톤 패턴을 적용하기 위해서 많은 코드들을 작성해야한다. (이를테면, private으로 생성자를 막아두는 방식) 이러한 특성을 두고, 스프링 컨테이너를 싱글톤 레지스트리
라고 한다.
싱글톤 객체들을 생성하고 관리하는 기능을
싱글톤 레지스트리
라고 한다.
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
//1번
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
//1번
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
//2번? 3번?
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
예를 들어, 위와 같은 코드가 있다고 가정해보자. AppConfig
클래스에서는 여러 빈들을 등록해두었다. 근데 MemberService
나 OrderService
객체에서는 다시 memberRepository()
를 호출하므로, memberRepository
객체가 싱글톤 객체로 유지되지 않는 것이 코드의 정상적인 플로우이다.
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
하지만, 실제로는 위의 출력문과 같이, memberRepository()
함수가 한 번만 호출되며, 싱글톤 패턴을 유지하는 것을 확인할 수 있다.
이유는 @Configuration
어노테이션에 있다.
@Configuration
어노테이션이 없으면, 아래의 출력문을 보면 알 수 있듯이 빈 객체들을 싱글톤 패턴으로 유지시켜주지 못한다.
call AppConfig.memberService
call AppConfig.memberRepository
call AppConfig.orderService
call AppConfig.memberRepository
call AppConfig.memberRepository
즉,
@Configuration
어노테이션은 바이트코드 조작을 통해, 스프링 컨테이너에서 빈 객체들을 싱글톤 객체로 유지할 수 있게 해준다.
CGLIB 라이브러리는 스프링 컨테이너에서 빈 객체들을 싱글톤 객체로 유지시켜주기 위한, 바이트코드 조작 라이브러리이다.
원래 위의 코드에서 new MemberRepository()
가 3번 호출되는 것이 맞다. 하지만, @Configuration
을 통해 싱글톤 객체로 유지된 것이다. 테스트 코드로 한번 확인해보자!
@Test
void configurationDeep() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
//AppConfig도 스프링 빈으로 등록된다.
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println("bean = " + bean.getClass());
}
AnnotationConfigApplicationContext
에 파라미터로 넘긴 값은 스프링 빈으로 등록된다. 그래서 AppConfig
클래스도 스프링 빈이 된다.위의 코드는 AppConfig
빈을 조회해보는 코드이다. 순수한 클래스라면, class hello.core.AppConfig
라고 조회되어야 한다.
하지만, 실제 출력문은 아래와 같았다.
bean = class hello.core.AppConfig$$EnhancerBySpringCGLIB$$bd479d70
예상과는 달리, xxxxxCGLIB라는 값이 붙으면서 상당히 복잡해진 것을 확인할 수 있다. 이것은 내가 만든 클래스가 아니라, 스프링이 CGLIB
라는 바이트조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스(AppConfig@CGLIB)를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다.
AppConfig
를 상속받았으므로, AppConfig
타입으로 빈을 조회하면, AppConfig@CGLIB
이 조회되는 것이다.Object
객체로 빈 조회 시 모든 빈 객체들이 조회된다.AppConfig@CGLIB
)가 싱글톤이 보장되도록 도와주는 것이다.정리하자면, @Configuration
어노테이션이 붙은 클래스 내에서 등록된 빈들은 CGLIB
라이브러리를 통해 싱글톤 패턴을 유지할 수 있다. 하지만, @Configuration
어노테이션이 없다면 CGLIB
라이브러리가 동작하지 않고, 순수한 클래스로 스프링 컨테이너에 빈으로 등록되어 싱글톤 패턴을 유지하지 못하게 된다.
https://sorjfkrh5078.tistory.com/283
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard