TIL

빈 후처리기

빈 후처리기를 사용하지 않고 동적 프록시를 등록하려면

BeanPostProcessor

빈 후처리기 과정

img.png

  1. 생성: 스프링 빈 대상이 되는 객체 생성 (@Bean, 컴포넌트 스캔 모두 포함)
  2. 전달: 생성된 객체를 빈 저장소에 등록하기 직전에 후 처리기에 전달
  3. 후 처리 작업: 전달된 스프링 객체를 조작하거나 다른 객체로 바꿔치기 할 수 있다
  4. 등록: 빈 후처리기는 빈을 반환한다. 반환된 빈은 빈 저장소에 등록 된다.

BeanPostProcessor 인터페이스

public interface BeanPostProcessor {
		Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
		Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
}
@Bean
public AToBPostProcessor helloPostProcessor() {
		return new AToBPostProcessor();
}

static class AToBPostProcessor implements BeanPostProcessor {

		@Override
		public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
			if (bean instanceof A) {
				 return new B();
			}
			return bean;
		}
}

@PostConstruct의 비밀 스프링은 CommonAnnotationBeanPostProcessor라는 빈 후처리기를 자동으로 등록하는데, 여기에서 @PostConstruct 애노테이션이 붙은 메서드를 호출한다. 따라서 스프링 스스로도 스프링 내부의 기능을 확장하기 위해 빈 후처리기를 사용한다.

빈 후처리기로 프록시를 생성한다면 프록시를 생성하는 부분을 하나로 집중할 수 있다. 그리고 컴포넌트 스캔처럼 스프링이 직접 대상을 빈으로 등록하는 경우에도 중간에 빈 등록 과정을 가로채서 원본 대신에 프록시를 스프링빈으로 등록할 수 있다.

스프링이 제공하는 빈 후처리기

스프링은 프록시를 생성하기 위한 빈 후처리기를 이미 만들어서 제공한다.

build.gradle 추가

implementation 'org.springframework.boot:spring-boot-starter-aop'

라이브러리를 추가하면 aspectjweaver라는 aspectJ 관련 라이브러리를 등록하고, 스프링 부트가 AOP 관련 클래스를 자동으로 스프링 빈에 등록한다.

자동 프록시 생성기 - AutoProxyCreator

참고 AnnotationAwareAspectJAutoProxyCreator@AspectJ와 관련된 AOP 기능도 자동으로 찾아서 처리해준다. Advisor는 물론이고, @Aspect도 자동으로 인식해서 프록시를 만들고 AOP를 적용해준다.

img.png

  1. 생성: 스프링이 스프링 빈 대상이 되는 객체를 생성한다. ( @Bean , 컴포넌트 스캔 모두 포함)
  2. 전달: 생성된 객체를 빈 저장소에 등록하기 직전에 빈 후처리기에 전달한다.
  3. 모든 Advisor 빈 조회: 자동 프록시 생성기는는 스프링 컨테이너에서 모든 Advisor를 조회한다.
  4. 프록시 적용 대상 체크: 앞서 조회한 Advisor에 포함되어 있는 포인트컷을 사용해서 해당 객체가 프록시를 적용할 대상인지 아닌지 판단한다. 이때 객체의 클래스 정보는 물론이고, 해당 객체의 모든 메서드를 포인트컷에 하나하나 모두 매칭해본다.
  5. 프록시 생성: 프록시 적용 대상이면 프록시를 생성하고 반환해서 빈으로 등록한다.
  6. 빈 등록: 반환된 객체는 스프링 빈으로 등록된다.
    • 프록시는 내부에 어드바이저와 실제 호출한 대상 객체(target)을 알고 있다.

포인트컷은 2가지에 사용된다.

  1. 프록시 적용 여부 판단 - 생성 단계
  2. 해당 빈이 프록시를 생성할 필요가 있는지 없는지 체크한다.
  3. 클래스, 메서드 조건을 모두 비교하여 하나라도 있으면 프록시를 생성한다.
  4. 만약 조건에 맞는 것이 하나도 없으면 프록시를 생성할 필요가 없으므로 프록시를 생성하지 않는다.
  5. 어드바이스 적용 여부 판단 - 사용 단계
  6. 프록시가 호출되었을 때 기능인 어드바이스를 적용할지 말지 포인트컷을 보고 판단한다.
    • 참고: 프록시를 모든 곳에 생성하는 것은 낭비이다. 자동 프록시 생성기는 포인트컷으로 한 번 필터링해서 어드바이스가 사용될 가능성이 있는 곳에만 프록시를 생성한다.

AspectJExpressionPointcut

AspectJ라는 AOP에 특화된 포인트컷 표현식을 적용할 수 있다. 지금은 특별한 표현식으로 복잡한 포인트컷을 만들 수 있구나 라고 대략 이해하면 된다.

@Bean
public Advisor advisor3(LogTrace logTrace) {
    //pointcut
    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    pointcut.setExpression("execution(* hello.proxy.app..*(..)) && !execution(* hello.proxy.app..noLog(..))");
    
		//advice
    LogTraceAdvice advice = new LogTraceAdvice(logTrace);
    return new DefaultPointcutAdvisor(pointcut, advice);
}

하나의 프록시, 여러 Advisor 적용

프록시 자동 생성기는 프록시를 하나만 생성한다. 왜냐하면 프록시 팩토리가 생성하는 프록시는 내부에 여러 advisor 들을 포함할 수 있기 때문이다.

정리

자동 프록시 생성기인 AnnotationAwareAspectJAutoProxyCreator 덕분에 개발자는 매우 편리하게 프록시를 적용할 수 있다. 이제 Advisor만 스프링 빈으로 등록하면 된다.