Spring @Configuration

강정우·2023년 11월 2일
0

Spring-boot

목록 보기
11/73
post-thumbnail

@Configuration

  • @Configuration은 사실 싱글톤을 위해 존재하는 것이다.
@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(getMemberRepository());
    }
    @Bean
    public static MemoryMemberRepository getMemberRepository() {
        return new MemoryMemberRepository();
    }
    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(getMemberRepository(), discountPolicy());
    }
    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
//        return new FixDiscountPolicy();
    }
}
  • 우리가 앞서 작성했던 AppConfig 코드를 봐보자. 우선
    @Bean memvberService가 -> new MemberyMemberRepository() 를 호출하고
    @Bean orderService가 -> new MemberyMemberRepository() 가 또 호출한다.
    그럼 2번 new 생성자 함수가 호출되었으니 싱글톤이 깨지는 것이 아닌가? 하고 생각할 수 있다.
    그렇다면 스프링 컨테이너는 이를 어떻게 해결할까? => 바이트코드 조작의 마법

  • 우선 그전에 문제를 하나 봐보자.

public class AppConfig {
    @Bean
    public MemberService memberService() {
        System.out.println("call MemberService");
        return new MemberServiceImpl(getMemberRepository());
    }
    @Bean
    public static MemoryMemberRepository getMemberRepository() {
        System.out.println("call MemoryMemberRepository");
        return new MemoryMemberRepository();
    }
    @Bean
    public OrderService orderService() {
        System.out.println("call OrderService");
        return new OrderServiceImpl(getMemberRepository(), discountPolicy());
    }
    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
//        return new FixDiscountPolicy();
    }
}
  • 자 우선 문제 spring container가 싱글톤으로 관리한다고 하니 MemoryMemberRepository 인스턴스의 주소값은 모두 같다 그럼 각각의 인스턴스가 생성되는 곳에 pring문을 찍어봤는데 어떻게 결과가 나올까? 과연 getMemberRepository이 메서드가 3번 실행될까?

  • 보면 getMemberRepository 메서드는 딱 1번 실행되는 것을 볼 수 있다.
    어떻게 그럴 수 있을까? => 바이트코드 조작의 마법

@Configuration & 바이트코드 조작의 마법

  • 스프링 컨테이너는 싱글톤 레지스트리다.
    따라서 스프링 빈이 싱글톤이 되도록 보장해주어야한다.
    그런데 스프링이 자바 코드까지 어떻게 하기는 어렵다.
    즉, 위 문제에서 getMemberRepository 이 메서드는 3번 실행이 되었어야 했다는 것이다.

  • 그런데 스프링은 클래스의 바이트ㅗ드를 조작하는 라이브러리를 사용한다.
    그것은 @Configuration을 적용한 AppConfig에 있다.

ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
  • 참고로 위 코드처럼 AnnotationConfigApplicationContext 의 변수로 넘기면 해당 변수도 @Bean으로 등록이 된다.
@Test
void configurationDeep(){
  ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
  AppConfig bean = ac.getBean(AppConfig.class);
  System.out.println("bean = " + bean.getClass());

}
  • 위 AppConfig를 위와같은 테스트 코드에 넣고 돌리면 이러한 결과가 나온다. 자세히 보면 core.AppCinfig뒤에 이상한게 막 붙어있는 것을 확인할 수 있다.

  • 원래 순수 클래스라면 class hello.core.AppConfig라고 나와야 정상이다. 그런데 이상하게 씨쥐리브(CGLIB)가 붙으면서 이상한 클래스가 튀어나온 것을 확인할 수 있는데,
    이것은 스프링이 CGLIB이라는 "바이트 코드 조작 라이브러리" 를 사용해서
    AppConfig 클래스를 상속받는 임의의 다른 클래스를 만들고,
    그 다른 클래스를 스프링 빈으로 등록한 것이다.

  • 그래서 사실 내가 작성한 진짜 AppConfig는 사라지고 스프링이 상속한 AppConfig@CGLIB이 등록되는 것이다.
    그래서 이 AppConfig@CGLIB이 싱글톤이 되도록 보장해주는 것이다.

  • 그럼 또 똑똑한 학생은

ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

아니 그럼 여기서 내가 작성한 자바 코드인 본래의 AppConfig가 나와야하는거 아니냐~ 할 수 있는데
위 사진에서 볼 수 있듯 AppConfig를 상속받은 AppConfigCGLIB이 AppConfig이름도 갖고가서 등록해버려서 AppConfig를 조회해도 앞서 언급했던 것 처럼 부모를 조회하면 자식이 다 딸려나와서 AppConfigCGLIB이 나오는 것인데 우리가 작성한 AppConfig가 나오는 것 처럼 보이는 것이다.

@Configuration을 붙이면 바이트코드를 조작하여 싱글톤을 보장하지만 만약 @Bean만 적용하면 어떻게 될까?

  • => 물론 안 해도 되긴 하다. 그러나 문제가 있다. 무슨 문제? 싱글톤이 깨진다.
    바이트코드를 조작하지 않아서 우리가 작성한 진짜 순수의 클래스가 나온다.

  • 즉, 스프링컨테이너에서 관리하는 객체가 아니게 된것이다.

  • 참고로 IDEA IDE 유료버전에서는 이렇게 친절하게 @Bean을 직접 참조했다고 알려준다. DI가 아니기 때문에 스프링이 관리하는 것이 아니게 되어버린다는 뜻이다.

  • 참고로 Autowired를 사용하면 스프링 빈으로 등록하고 그걸 다시 끌어와서 여기다 넣어준다. 아래 결과를 보면 주소값(memberRepository 2개)도 같은 것을 확인할 수 있다.
    그런데 이는 뒤에서 다시 포스팅하겠다.

@Bean만 사용해도 스프링 빈으로 등록이 되긴하는데 싱글톤이 보장되지 않는다.
특히 AppConfig에서 의존관계 주입이 필요해서 메서드를 직접 호출할 때 싱글톤을 보장하지 않는다.

profile
智(지)! 德(덕)! 體(체)!

0개의 댓글