이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • BeanFactoryPostProcessor와 BeanPostProcessor는 어느 단계에 개입하는가?
  • Spring의 애너테이션 처리와 AOP Proxy는 왜 생명주기 확장 포인트와 연결되는가?
  • 직접 후처리기를 만들 때 무엇을 조심해야 하는가?

개요

Spring Container는 단순히 Bean을 생성하는 고정된 기계가 아니다. BeanDefinition 단계와 Bean 인스턴스 단계에 확장 포인트를 제공한다. 이 확장 포인트 위에서 많은 Spring 기능이 구현된다.

BeanFactoryPostProcessor는 Bean 인스턴스 생성 전 BeanDefinition을 조작할 수 있고, BeanPostProcessor는 Bean 인스턴스 생성 후 초기화 전후에 개입할 수 있다.

원리

두 확장 포인트의 차이는 개입 대상이다.

BeanDefinition 등록
BeanFactoryPostProcessor
Bean 인스턴스 생성
의존성 주입
BeanPostProcessor before initialization
초기화 callback
BeanPostProcessor after initialization

BeanFactoryPostProcessor는 아직 객체가 만들어지기 전 정의 정보를 다룬다. BeanPostProcessor는 객체가 만들어진 뒤 그 객체를 검사하거나 감싸거나 교체할 수 있다.

BeanFactoryPostProcessor

BeanFactoryPostProcessor는 BeanDefinition 메타데이터를 바꿀 수 있다. 예를 들어 placeholder를 실제 환경 값으로 해석하거나, 특정 BeanDefinition의 속성을 조정하는 일이 가능하다.

이 단계에서는 일반 Bean 인스턴스를 다루지 않는다는 점이 중요하다. 너무 이른 단계에서 Bean을 강제로 조회하면 조기 초기화가 일어나고, 해당 Bean이 이후의 후처리나 자동 프록시 대상에서 빠질 수 있다.

BeanPostProcessor

BeanPostProcessor는 Bean 인스턴스 초기화 전후에 호출된다. 이 확장 포인트는 강력하다. Bean을 그대로 반환할 수도 있고, Proxy로 감싸서 반환할 수도 있다. BeanPostProcessor는 등록된 컨테이너 안의 Bean에만 적용되므로 parent-child context나 테스트 context를 볼 때는 어느 컨테이너에 등록되었는지도 중요하다.

AOP 자동 프록시 생성도 이 관점으로 이해하면 좋다. 컨테이너는 Bean을 만들고 후처리 단계에서 Pointcut 대상인지 검사한 뒤, 필요하면 원본 Bean 대신 Proxy를 노출한다.

후처리기 자체와 후처리기가 직접 의존하는 Bean은 일반 애플리케이션 Bean보다 이른 시점에 생성된다. 그래서 후처리기 안에서 많은 Bean을 끌어오면 그 Bean들이 모든 후처리와 자동 프록시 적용을 받지 못하는 문제가 생길 수 있다.

애너테이션 처리와의 연결

@Autowired, @PostConstruct, AOP, @Async, @Transactional 같은 기능은 단순 문법이 아니라 컨테이너 후처리와 연결되어 있다. 애너테이션 자체가 실행되는 것이 아니라, 해당 애너테이션을 해석하는 후처리기가 Bean 생성 과정에 개입한다.

이 관점을 가지면 “왜 직접 new 한 객체에는 @Autowired가 안 되나?” 같은 질문이 풀린다. 직접 만든 객체는 컨테이너의 Bean 생성과 후처리 흐름을 지나지 않는다.

실전 팁

  • BeanPostProcessor는 프레임워크를 만드는 도구에 가깝다. 애플리케이션 비즈니스 기능을 해결하려고 먼저 꺼낼 도구는 아니다.
  • AOP가 안 먹는 문제는 Pointcut뿐 아니라 Bean 후처리 흐름을 지났는지 확인해야 한다.
  • 직접 후처리기를 만들면 모든 Bean에 영향을 줄 수 있으므로 대상 필터링을 매우 좁게 둔다.
  • 후처리기 안에서 다른 Bean을 무분별하게 조회하면 조기 초기화 문제가 생길 수 있다.
  • @Bean 메서드로 후처리기를 등록할 때는 반환 타입이 BeanPostProcessor 타입으로 드러나게 하고, 가능하면 의존성을 가볍게 유지한다.
  • “애너테이션을 붙였는데 왜 동작하지 않지?”라는 문제는 대개 그 애너테이션을 처리하는 후처리기가 등록되었는지, 대상 객체가 Bean인지와 연결된다.

위험 신호!

  • BeanPostProcessor로 비즈니스 규칙을 구현한다.
  • 모든 Bean을 대상으로 무거운 reflection 검사를 수행한다.
  • 후처리기 안에서 Bean을 강제로 조회해 초기화 순서를 꼬이게 만든다.
  • 후처리기 자체가 무거운 의존성을 끌어와 여러 Bean을 자동 프록시 대상에서 빠뜨린다.
  • Proxy로 감싼 뒤 원본 타입 가정을 깨뜨린다.
  • BeanDefinition 단계와 Bean 인스턴스 단계를 구분하지 못한다.

확인 질문

확인 질문

  • BeanFactoryPostProcessor는 무엇을 다루는가?
    • Bean 인스턴스가 아니라 BeanDefinition 메타데이터를 다룬다.
  • BeanPostProcessor는 어느 단계에 개입하는가?
    • Bean 인스턴스 생성 후 초기화 전후 단계에 개입한다.
  • AOP Proxy와 BeanPostProcessor는 어떤 관계인가?
    • Bean 후처리 단계에서 대상 Bean을 Proxy로 감싸 노출할 수 있다는 점에서 연결된다.

참고 문서