본문 바로가기

SpringFramework Core - I. IoC 컨테이너/8. 컨테이너의 확장가능한 부분들

1.8.1. BeanPostProcessor로 Bean 커스터마이징하기

BeanPostProcessor 인터페이스는 나만의 인스턴스화 로직, 의존성 로직 등을 제공하게 해주는, 또는 컨테이너의 기본값들을 오버라이드하게 해주는 콜백 메소드를 정의한다. 만약 스프링 컨테이너가 인스턴스화, 설정, bean 초기화를 마친 후에 커스텀한 로직을 실행하게하고 싶다면, 하나 이상의 커스텀한 BeanPostProcessor에 플러그인하면 된다.

 

여러 개의 BeanPostProcessor 인스턴스를 설정할 수 있다. 그리고 이 BeanPostProcessor들이 설정된 order 프로퍼티에 따라 실행되도록 명령을 제어할 수 있다. 이 프로퍼티는 오직 BeanPostProcessor가 Ordered 인터페이스를 구현했을 때에만 세팅할 수 있다. 만약 자신만의 BeanPostProcessor를 작성한다면 Ordered 인터페이스를 구현해야한다는 점을 고려해야한다. 더 자세한 내용은 BeanPostProcessor와 Ordered 인터페이스의 자바 문서를 참고하라. 또한 'BeanPostProcessor 인스턴스들의 프로그램적인 등록'을 참고하라.

 

 

  BeanPostProcessor 인스턴스들은 bean 인스턴스들에 대해 작동한다. 즉, 스프링 IoC 컨테이너가 bean 인스턴스를 인스턴스화하고나서 BeanPostProcessor 인스턴스들은 자기 일을 한다. 

 

BeanPostProcessor 인스턴스들은 컨테이너별로 scope가 할당된다. 이것은 계층적 컨테이너를 사용할때만 관련이 있다. 만약 하나의 컨테이너에서 BeanPostProcessor를 정의한다면 그 컨테이너의 bean들에 대해서만 후처리를 하게 될 것이다. 즉, BeanPostProcessor와는 다른 컨테이너에서 정의된 bean들은 후처리가 되지 않는다. 심지어 그 컨테이너가 같은 계층에서의 부분들이라 할지라도 마찬가지다. 

 

bean 정의를 실질적으로 변경하기 위해서는 BeanFactoryPostProcessor를 사용해야한다. 'BeanFactoryPostProcessor로 설정 메타데이터 커스터마이징하기'를 참고하라.

 

 

org.springframework.beans.factory.config.BeanPostProcessor 인터페이스는 정확히 두 개의 콜백 메소드로 구성된다. 컨테이너에 해당 클래스가 후처리자로 등록되면, 후처리자는 같은 컨테이너에서 생성된 각각의 bean 인스턴스들에 대해 bean 초기화 메소드가 호출되기 전과 후에 컨테이너로부터 콜백을 호출한다. 후처리자는 bean 인스턴스에 대해 콜백을 완전히 무시하는 것과 같은 어떠한 처리들도 할 수 있다. bean의 후처리자는 일반적으로 콜백 인터페이스를 체크하거나 bean을 프록시로 감싼다. 몇몇 스프링 AOP 인프라의 클래스들은 프록시로 감싸는 로직을 제공하기 위해 후처리자 bean으로 구현된다. 

 

ApplicationContext는 자동적으로 설정 메타데이터 안에서 BeanPostProcessor 인터페이스를 구현한 bean들을 찾아낸다. ApplicationContext는 이 bean들을 후처리자로 등록하여 그들이 나중에(bean 생성 시점) 호출될 수 있도록 한다. Bean 후처리자는 다른 bean들과 마찬가지의 방법으로 컨테이너 안에 설정될 수 있다.

 

설정 클래스에서 @Bean 팩토리 메서드를 통해 BeanPostProcessor를 정의할 때는, 팩토리 메서드의 반환 타입이 구현 클래스 그 자체여야 하거나 최소한 org.springframework.beans.factory.config.BeanPostProcessor 인터페이스여야 한다는 점을 주의하라. 그렇지 않으면, ApplicationContext는 후처리자를 완전히 생성해내기 전까지 그것을 자동으로 찾아내지 못한다. BeanPostProcessor는 context 안의 다른 bean들의 초기화에 적용될 수 있도록 더 빠른 시점에 초기화될 필요가 있기 때문에 빠른 타입 감지는 매우 중요하다.

 

 

  BeanPostProcessor 인스턴스들의 프로그램적인 등록

BeanPostProcessor 등록의 권장되는 방법은 ApplicationContext의 자동감지를 통한 방법이다. 하지만 후처리자들을 ConfigurableBeanFactory의 addBeanPostProcessor를 사용하여 프로그램적으로 등록할 수도 있다. 이 방법은 등록 전에 조건을 따져봐야할 필요가 있거나 계층적 context들 사이에서 후처리자 bean을 복사하여 사용하려고 할 때 유용할 것이다. 그러나 프로그램적으로 추가된 BeanPostProcessor 인스턴스들은 Ordered 인터페이스를 따르지 않는다는 점을 주의하라. 이제, 등록의 순서가 실행의 순서를 결정한다. 프로그램적으로 등록된 BeanPostProcessor 인스턴스들은 항상 자동 감지에 의해 등록된 후처리자들보다 먼저 작동한다는 점 또한 주의하라. 이때 명확히 부여한 순서도 무시된다.

 

 

  BeanPostProcessor 인스턴스들과 AOP auto-proxying

BeanPostProcessor 인터페이스를 구현하는 클래스들은 특별하며 컨테이너에게 다른 취급을 받는다. 모든 BeanPostProcessor 인스턴스들과 그들이 직접 참조하는 bean들은 startup 시점에 ApplicationContext의 특별한 startup 단계의 일부가 되어 인스턴스화된다. 그 다음에, 모든 BeanPostProcessor 인스턴스들은 분류되어 등록되고 컨테이너의 다른 모든 bean들에게 적용된다. AOP auto-proxing은 스스로가 BeanPostProcessor를 구현하고 있기 때문에 BeanPostProcessor 인스턴스들이나 그들이 직접 참조하는 bean들에 대해서나 auto-proxing의 대상으로 삼을 수 없다. 그래서 aspect들을 그들 안으로 위빙할 수 없다.

 

그런 bean에 대해서는 다음과 같은 정보 로그 메시지를 보게 될 것이다. Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying).

 

만약 autowiring이나 @Resource를 사용함으로써 bean들을 BeanPostProcessor 안으로 엮었다면, 스프링은 타입이 일치하는 의존성 후보를 찾는 과정에서 예상치 못한 bean들로 접근할 수도 있다. 그리고 그들을 auto-proxing이나 다른 종유의 후처리 bean에 대해 적용 불가능하게 만들지도 모른다. 예를 들어, 필드에 @Resource라는 어노테이션이 붙었거나 setter 이름이 선언된 bean의 이름과 직접적으로 일치하지 않고 이름 속성이 쓰이지 않았다면, 스프링은 타입을 통해 그들을 다른 bean과 매칭시키려 할 것이다.

 

 

다음 예시들은 ApplicationContext 안에서 어떻게 BeanPostProcessor 인스턴스들을 작성하고 등록하며 사용하는지 보여준다.