본문 바로가기
Spring

@Configuration 과 Singleton

by Heesu.lee 2020. 12. 7.

AppConfig.class

위 코드는 설정 정보를 자바 코드로 작성한 AppConfig 클래스 이다

  • memberService 빈을 만드는 코드를 보면 'memberService()' 를 호출한다.
    • 해당 메서드를 호출하면 'new MemoryMemberRepository()' 를 호출한다.
  • orderService 빈을 만든느 코드도 동일하게 'memberRepository()' 를 호출한다.
    • 해당 메서드 역시 호출 시에 'new MemoryMemberRepository()' 를 호출한다.

결과적으로 각각 다른 2개의 'MemoryMemberRepository' 가 생성되면서 싱글톤 보장이 깨지는 것 처럼 보인다.

그렇다면 스프링 컨테이너는 이 문제를 어떻게 해결할까?

싱글톤 보장 여부 확인 테스트 코드
테스트 결과

AppConfig 의 자바 코드를 보면 분명히 각각 2번의 호출에 의해 'new MemoryMemberRepository' 가 실행되어 다른 인스턴스가 생성되어야 하는데, 확인 결과 memberRepository 인스턴스는 모두 같은 인스턴스가 공유되어 사용되는 것을 확인할 수 있다.

이 모든 마법의 비밀은 @Configuration바이트코드 조작 에 있다.

 

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

스프링 컨테이너는 싱글톤 레지스트리다. 따라서 스프링 빈이 싱글톤이 되도록 보장해주어야 한다. 그런데 스프링이 사용자가 정의한 자바 코드까지 어떻게 하기는 어렵다. 위 처럼 사용자가 정의한 AppConfig 자바 코드를 보면 분명 3번 호출되어야 하는 것이 맞다.

모든 비밀은 '@Configuration' 을 적용한 'AppConfig' 에 있다.

 

스프링은 클래스의 바이트코드 를 조작하는 라이브러리를 사용하여 싱글톤을 보장하고자 한다.

@Configuration 을 적용한 AppConfig 테스트

참고 : AnnotationConfigApplicationContext 에 파라미터로 넘긴 값은 스프링 빈으로 등록된다. 따라서, AppConfig 역시 스프링 빈이 된다.

위 테스트를 통해 AppConfig 스프링 빈을 조회해서 클래스 정보를 출력해보면 다음 아래와 같다.

getBean을 통한 AppConfig 클래스 정보 조회

만약 순수한 클래스라면 위 결과는 다음과 같이 출력되어야 한다.

'class hello.core.AppConfig'

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

 

CGLIB 로 바이트코드가 조작된 새롭게 탄생한 임의의 AppConfig 클래스

위와 같이 CGLIB 를 통해 생성된 임의의 AppConfig 클래스가 바로 싱글톤이 보장되도록 해주게 되는 것이다.

(실제 CGLIB 의 내부 기술은 복잡하므로 따로 다루진 않겠지만, Spring AOP 에서 동일하게 사용되는 기술이다.)

 

참고

  • 해당 게시글은 김영한님의 스프링 핵심 원리 - 기본편을 바탕으로 제작되었음을 알려드립니다.

댓글