Spring @Configuration에 관하여 (feat. CGLIB Proxy)
Spring @Configuration에 관하여 (feat. CGLIB Proxy)
개요
스프링 컨테이너에 Bean을 등록하는 방법은 다양하다.
그 중 하나가 @Configuration 설정 정보를 이용하여 @Bean 정의된 메서드를 호출하여 등록하는 방법이 있다.
위 코드를 살펴보자.
Spring은 @Bean 정의가 되어있는 메서드들을 호출함으로써
- 빈 생성 및 등록
- 빈 타입 : 반환 타입(
Book,Magazine,Title,Author) - 빈 이름 : 메서드 명(
myBook,myMagazine,title,author)
- 빈 타입 : 반환 타입(
와 같이 빈을 생성하여 컨테이너에 등록함으로써 싱글톤으로 관리한다.
근데 의아한 점이 있다.
myBook()메서드를 호출하면Book인스턴스를 생성하면서title(),author()를 호출한다.myMagazine()메서드를 호출하면Magazine인스턴스를 생성하면서title(),author()를 호출한다.
그렇다면 myBook 빈이 갖고있는 Title, Author 인스턴스와 myMagazine 빈이 갖고있는 Title, Author 인스턴스는 다른 인스턴스인가? 같은 인스턴스인가?
결론부터 이야기 하자면 동일한 인스턴스이다. 즉, **스프링 컨테이너에서 싱글톤으로 관리하고 있는 title 빈, author 빈**이 주입된다.
어떻게 이게 가능한 것일까? 우리가 알고있는 자바 코드라면 title(), author() 호출 시 마다 새로운 인스턴스를 반환해야 하기 때문에 서로 다른 인스턴스가 주입되어야 하는데?
비밀은 @Configuration에 있다.
인용 : Method invocations in a Spring @Configuration class don’t follow the regular Java semantics.
@Configuration과 CGLIB
인용 문구 그대로 @Configuration이 정의된 클래스의 메서드 호출은 일반 자바 시맨틱을 따르지 않는다.
스프링은 @Configuration이 정의된 클래스를 먼저 스프링 빈으로 등록하고 @Bean 메서드를 호출함으로써 나머지 스프링 빈을 등록한다.
근데 @Configuration 클래스를 그대로 사용하는것이 아니라 CGLIB이라는 바이트 코드 조작 라이브러리를 사용하여 **@Configuration 클래스의 proxy를 스프링 빈으로 등록한다.**
이렇게 CGLIB proxy가 된 스프링 빈은 title(), author()와 같이 메서드가 호출될 때 이미 컨테이너에 해당 이름을 가진 스프링 빈이 존재하는지 확인을 한다.
- 만일 존재한다면 메서드 실행을 하지 않고 존재하는 빈을 그대로 주입한다.
- 만일 존재하지 않는다면 메서드 실행을 통해 스프링 빈으로 등록 및 주입해준다.
위와 같이 CGLIB proxy의 바이트 코드 조작을 이용하기 때문에 실제 title(), author()의 호출 횟수는 1 번씩 이뤄지게 되는 것이다.
만일 @Configuration이 없다면?
위와 같이 @Configuration을 주석 처리한다면 어떻게 될까?
이렇게 되면 CGLIB proxy가 아닌 BookConfiguration 타입의 인스턴스가 스프링 빈으로 등록되고 각 @Bean을 호출한다.
CGLIB proxy가 아니기 때문에 자바 코드 그대로 실행하게 된다. 즉, title(), author() 메서드는 각각 3 번 씩(1번은 Bean 생성 및 등록 시, 2 번은 myBook(), myMagazine() 호출 시) 호출된다.
따라서 myBook 빈의 Author, Title의 인스턴스와 myMagazine 빈의 Author, Title의 인스턴스는 스프링 빈이 아닌 서로 다른 일반 인스턴스이다.