✔️ 빈 후처리기 - BeanPostProcessor
스프링이 빈 저장소에 등록할 목적으로 생성한 객체를 빈 저장소에 등록하기 직전에조작이 가능하다.
한 마디로 빈 생성후무언가 처리하는 용도이다.
이를 사용하면 컴포넌트 스캔 대상들도 프록시로 등록할 수 있게 된다.
BeanPostProcessor 인터페이스 - 스프링 제공
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
}
빈 후처리기를 사용하려면 해당 인터페이스 구현 후 스프링 빈 등록
postProcessBeforeInitialization : 객체 생성 이후 @PostConstruct 같은 초기화가 발생 전에 호출되는 포스트 프로세서
postProcessAfterInitialization : 객체 생성 이후 @PostConstruct 같은 초기화가 발생한 다음에 호출되는 포스트 프로세서
참고
스프링은 CommonAnnotationBeanPostProcessor 라는 빈 후처리기를 자동으로 등록하는데, 여기에서
@PostConstruct 애노테이션이 붙은 메서드를 호출한다. 따라서 스프링 스스로도 스프링 내부의 기능을 확장
하기 위해 빈 후처리기를 사용한다.
적용
@Slf4j
public class PackageLogTracePostProcessor implements BeanPostProcessor {
private final String basePackage;
private final Advisor advisor;
public PackageLogTracePostProcessor(String basePackage, Advisor advisor) {
this.basePackage = basePackage;
this.advisor = advisor;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
log.info("param beanName={}, bean={}", beanName, bean);
//프록시 적용 대상 여부 체크
// 프록시 적용 대상이 아니면 원본을 그대로 진행
String packageName = bean.getClass().getPackageName();
if (!packageName.startsWith(basePackage)) {
return bean;
}
// 프록시 대상이면 프록시를 만들어서 반환
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.addAdvisor(advisor);
Object proxy = proxyFactory.getProxy();
return proxy;
}
}
패키지명으로 프록시 적용 대상을 체크하고 맞다면 프록시를 만들어서 반환, 아니라면 bean을 그대로 반환한다.
설정클래스를 만들어서 빈 후처리기를 등록한다. 이때 advisor도 만들어서 파라미터로 넘겨야한다.
@Slf4j
@Configuration
@Import({AppV1Config.class, AppV2Config.class}) // v3는 컴포넌트 스캔 대상이기에 알아서 등록됨
public class BeanPostProcessorConfig {
@Bean
public PackageLogTracePostProcessor logTracePostProcessor(LogTrace logTrace) {
return new PackageLogTracePostProcessor("hello.proxy.app", getAdvisor(logTrace));
}
private Advisor getAdvisor(LogTrace logTrace) {
//pointcut
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("request", "order*", "save*");
// advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
}
그런데 굳이 후처리기에서 적용대상을 체크할 필요없이 포인트 컷을 사용하면 깔끔하다.
✔️ 스프링이 제공하는 빈 후처리기
build.gradle - 추가
implementation 'org.springframework.boot:spring-boot-starter-aop'
aspectJ 라이브러리등록 (AOP 관련 클래스를 자동으로 스프링빈에 등록)
자동 프록시 생성기 -AutoProxyCreator
- 스프링 부트 자동 설정으로 AnnotationAwareAspectJAutoProxyCreator 라는 빈 후처리기가
스프링 빈에 자동으로 등록 - 이 빈 후처리기는 스프링 빈으로 등록된 Advisor 들을 자동으로 찾아서 프록시가 필요한 곳에자동으로 프록시를 적용
- Advisor의 포인트 컷 + Advice가 있으므로 프록시 적용 대상 구분 가능
- @AspectJ와 관련된 AOP 기능도 자동으로 찾아서 처리
자동 프록시 생성기 작동 과정
- 생성 : 스프링이 스프링 빈 대상을 객체로 생성 (@Bean, 컴포넌트 스캔 대상)
- 전달 : 생성된 객체를 빈 저장소등록 전 빈 후처리기에 전달
- 모든 Advisor 빈 조회 : 자동 프록시생성기 - 빈 후처리기는 스프링 컨테이너에서 모든 Advisor를 조회
- 프록시 적용 대상 체크 : advisor에 포함되어 있는 포인트 컷이 프록시 적용대상 체크 (클래스정보, 객체의 모든 메서드)
- 이때 하나라도 만족하면 프록시로생성
- 프록시 생성 : 프록시 적용 대상이면 프록시 생성 및 반환, 스프링 빈으로 등록
-
- 아니라면 원본 객체를 스프링 빈으로등록
-
- 빈 등록 : 반환된 객체는 스프링 빈으로 등록
여기서 여러 Advisor를 적용할 경우, Advisor의 포인트 컷 조건과 맞으면 적용할 수 있다.
예) advisor 1 의 조건 과 advisor 2 의 조건 따로따로 체크 후 맞는 것은 적용
따라서 1 만 적용, 1 , 2 적용, 2만 적용하는 경우가 생긴다.
스프링이 AnnotationAwareAspectJAutoProxyCreator 빈 후처리기를 자동으로 등록
따라서 Advisor만 빈으로 등록하면 된다.
❗중요 : 포인트컷은 2가지에 사용
1. 프록시 적용 여부 판단 - 생성 단계
- 프록시 생성 여부는클래스 + 메서드 조건 모두 비교하여 체크, 만약 포인트 컷 조건에 하나라도 만족한다면 프록시를 생성
- 만족하지 않는다면 생성 X
2. 어드바이스 적용 여부판단 - 사용 단계
- 부가 기능인 어드바이스를 적용할지 말지포인트컷을 보고 판단
프록시를 모든 곳에 생성하는 것은 비용 낭비이기에 최소한의 프록시를 적용
패키지에 메서드 이름까지 함께 지정할 수 있는 매우정밀한 포인트 컷
AspectJExpressionPointcut
AspectJ 라는 AOP 에특화된포인트 컷 표현식을 적용 가능 (AOP 챕터에서 자세히..)
@Bean
public Advisor advisor3(LogTrace logTrace) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.proxy.app..*(..)) &&
!execution(* hello.proxy.app..noLog(..))");
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
//advisor = pointcut + advice
return new DefaultPointcutAdvisor(pointcut, advice);
}
* : 모든 반환 타입
hello.proxy.app.. : 해당 패키지와 그 하위 패키지
*(..) : * 모든메서드 이름, (..) 파라미터는 상관 없음
🔖 학습내용 출처
'Back-End > Spring Advance & Boot' 카테고리의 다른 글
스프링 고급편 - 스프링 AOP 개념 (0) | 2024.10.04 |
---|---|
스프링 고급편 - @Aspect AOP (0) | 2024.10.02 |
스프링 고급편 - 스프링이 지원하는 프록시 (프록시 팩토리) (1) | 2024.09.25 |
스프링 고급편 - 동적 프록시 기술 (0) | 2024.09.24 |
스프링 고급편 - 프록시 패턴과 데코레이터 패턴 (1) | 2024.09.04 |