@Configuration 어노테이션과 바이트조작

땡글이·2023년 1월 15일
0

Spring Framework

목록 보기
1/8
post-thumbnail

스프링 컨테이너는 다들 알다싶이, 싱글톤 패턴을 적용시켜 빈 객체를 하나로 관리하여 메모리를 효율적으로 관리한다. 싱글톤 패턴은 메모리를 최적으로 유지할 수 있다는 장점이 있지만, 싱글톤 패턴을 적용하기 위해서 많은 코드들을 작성해야한다. (이를테면, private으로 생성자를 막아두는 방식) 이러한 특성을 두고, 스프링 컨테이너를 싱글톤 레지스트리라고 한다.

싱글톤 객체들을 생성하고 관리하는 기능을 싱글톤 레지스트리 라고 한다.

@Configuration 어노테이션

@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 클래스에서는 여러 빈들을 등록해두었다. 근데 MemberServiceOrderService 객체에서는 다시 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 라이브러리

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 라이브러리가 동작하지 않고, 순수한 클래스로 스프링 컨테이너에 빈으로 등록되어 싱글톤 패턴을 유지하지 못하게 된다.

Reference

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

profile
꾸벅 🙇‍♂️ 매일매일 한발씩 나아가자잇!

0개의 댓글