728x90

@Configuration 을 알아보기 위해서 이 코드를 살펴보자,

시나리오

1. @Bean memberService 를 호출하면 ➡️ MemoryMemberRepository() 를 한번 호출한다. 

2. @Bean orderService 를 호출하면 ➡️ MemoryMemberRepository() , discountPolicy() 를 호출한다.

 

이렇게 하면 결과적으로 각각 다른 2개의 MemoryMemberRepository() 가 두 번 호출 되니까 싱글톤이 깨지는게 아닐까요?? 스프링 컨테이너가 싱글톤을 보장해준다고 했는데 과연 그럴까?? 직접 테스트 해보자

 

✅ 테스트 방법

1. MemoryMemberRepository 를 불러오는 MemberServiceImpl 에서 

테스트 용도로 MemberServiceImpl, OrderServiceImpl 둘 다 각각 생성자로 들어오는 memberRepository 의 값을 확인하는 getMemberRepository 메서드를 만들었다.

 

2. 각각의 memberRepository 를 테스트 코드를 통해 확인해보자.

밑에는 테스트코드이고

밑에는 테스트 코드의 결과이다.

결과는 세개의 memberRepository가 같은 것을 확인할 수 있다. 분명 세번의 다른 memberRepository의 인스턴스가 호출될 것 같았지만 모두 같은 인스턴스가 공유되어 사용되고 있음을 확인할 수 있었다. 어떻게 된 일일까? 혹시 두 번 호출이 안되는 것일까? 다음 실험을 통해 알아보자. 

 

이렇게 호출 되는 부분마다 찍어보면서 확인해보았다. 

찍어본 세 개의 메서드 각각 한번씩 호출 되었는 것을 볼 수 있다. 이 비밀이 어디에 있을까?? ➡️ @Configuration 을 적용한 AppConfig 에 있다.

 

시나리오

1. AppConfig 스프링 빈을 조회해서 클래스 정보를 출력해보자.

순수한 클래스라면 다음과 같이 출력되어야 한다.

class hello.core.AppConfig

그런데 예상과는 다르게 클래스 명에 xxxCGLIB가 붙으면서 상당히 복잡해진 것을 볼 수 있다. 이것은 내가 만든 클래스가 아니라 스프링이 CGLIB 이라는 바이트코드 조작 라이브러리를 사용해서 AppConfig 클래스를 상속받은 임의의 다른 클래스를 만들고, 그 다른 클래스를 스프링 빈으로 등록한 것이다.

그 임의의 다른 클래스가 바로 싱글톤이 보장되도록 해준다. 아마도 다음과 같이 바이트 코드를 조작해서 작성되어 있을 것이다. (실제로는 CGLIB의 내부 기술은 매우 복잡하다.)

 

  • @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다.
  • 덕분에 싱글톤이 보장되는 것이다.

 

만약 @Configuration 을 적용하지 않고, @Bean 만 적용하면 어떻게 될까?

@Configuration 을 붙이면 바이트코드를 조작하는 CGLIB 기술을 사용해서 싱글톤을 보장하지만, 만약 @Bean만 적용하면 어떻게 될까?

 

@Configuration 을 주석처리 하고 @Bean 만 적용해보고 밑에 테스트 코드를 다시 돌려보았다.

결과로 이렇게 나왔다. 

 

CGLIB 기술을 사용 하지 않은 순수한 AppConfig 가 나왔다. 그리고

memberRepository가 여러번 호출되는 것을 확인할 수 있듯이 순수한 자바 코드가 나오면서 싱글톤이 깨진다!!

 

 

정리

  • @Bean만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지 않는다.
    • memberRepository() 처럼 의존관계 주입이 필요해서 메서드를 직접 호출할 때 싱글톤을 보장하지 않는다.
  • 크게 고민할 것이 없다. 스프링 설정 정보는 항상 @Configuration 을 사용하자.

 

+ Recent posts