⬜섹션7 빈 후처리기
◼️빈 후처리기
빈 후처리기(BeanPostProcessor)는 생성객체를 빈 저장소에 등록하기 직전 조작할 수 있다.
이를 사용하려면 아래 인터페이스를 구현후, 스프링 빈으로 등록하면 된다.
🔶 BeanPostProcessor 인터페이스
public interface BeanPostProcessor {
// 객체 생성 이후, @PostConstruct 같은 초기화 발생 전 호출되는 포스트 프로세서
Object postProcessBeforeInitialization(Object bean, String beanName) throws
BeansException
// 객체 생성 이후, @PostConstruct 같은 초기화 발생 후 호출되는 포스트 프로세서
Object postProcessAfterInitialization(Object bean, String beanName) throws
BeansException
}
◼️빈 후처리기 - 적용
빈 후처리기로 실제 객체 대신 프록시를 스프링 빈으로 등록할 수 있다.
설정 파일에 있는 수 많은 프록시 생성 코드도 한번에 제거할 수 있다.
아래와 같이 설정하면, 프록시를 생성하는 코드가 설정 파일에는 필요 없다.
@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.getClass());
//프록시 적용 대상 여부 체크
//프록시 적용 대상이 아니면 원본을 그대로 진행
String packageName = bean.getClass().getPackageName();
if (!packageName.startsWith(basePackage)) {
return bean;
}
//프록시 대상이면 프록시를 만들어서 반환
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.addAdvisor(advisor);
Object proxy = proxyFactory.getProxy();
log.info("create proxy: target={} proxy={}", bean.getClass(), proxy.getClass());
return proxy;
}
}
@Slf4j
@Configuration
@Import({AppV1Config.class, AppV2Config.class})
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);
}
}
@Import(BeanPostProcessorConfig.class)
@SpringBootApplication(scanBasePackages = "hello.proxy.app") //주의
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
@Bean
public LogTrace logTrace() {
return new ThreadLocalLogTrace();
}
}
◼️스프링이 제공하는 빈 후처리기
아래 라이브러리를 추가하면 `aspectjweaver` 라는 `aspectJ` 관련 라이브러리를 등록하고, 스프링 부트가 AOP 관련 클래스를 자동으로 스프링 빈에 등록한다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
🟢 자동 프록시 생성기 - AutoProxyCreator
자동 프록시 생성기 (AnnotationAwareAspectJAutoProxyCreator)는 스프링 빈으로 등록된 Advisor 들을 자동으로 찾아서 프록시가 필요한 곳에 자동으로 프록시 를 적용해준다.
🟢 자동 프록시 생성기 동작 과정
- 생성된 객체는 빈 저장소에 등록 전 빈 후처리기에 전달된다.
- 자동 프록시 생성기는 모든 Advisor 빈을 조회하고, 포인트컷을 사용해서 해당 객체가 프록시를 적용할 대상인지 아닌지 판단한다.
- 조건을 하나라도 만족시 프록시를 생성해 빈을 등록한다.
빈 후처리기를 따로 등록하지 않아도, 스프링은 자동 프록시 생성기라는(AnnotationAwareAspectJAutoProxyCreator) 빈 후처리기를 자동으로 등록해준다.
@Configuration
@Import({AppV1Config.class, AppV2Config.class})
public class AutoProxyConfig {
// @Bean
public Advisor advisor1(LogTrace logTrace) {
//pointcut
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("request*", "order*", "save*");
//advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
//@Import(AutoProxyConfig.class)
@Import(AopConfig.class)
@SpringBootApplication(scanBasePackages = "hello.proxy.app") //주의
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
@Bean
public LogTrace logTrace() {
return new ThreadLocalLogTrace();
}
}
🟢 포인트 컷은 두가지에 사용된다.
- 프록시 적용 여부 판단 - 생성 단계: 자동 프록시 생성기는 포인트컷을 사용해서 해당 빈이 프록시를 생성할 필요가 있는지 없는지 체크한다. (클래스 + 메서드 조건을 모두 비교)
- 어드바이스 적용 여부 판단 - 사용 단계: 프록시가 호출되었을 때 부가 기능인 어드바이스를 적용할지 말지 포인트컷을 보고 판단한다.
🙁 문제점
스프링이 내부에서 사용하는 빈에도 메서드 이름에 필터링 설정 단어만 들어가 있으면 프록시가 만들어지고 되고, 어드바이스도 적용된다.
🙂 해결책
패키지에 메서드 이름까지 함께 지정할 수 있는 매우 정밀한 포인트컷이 필요하다. AspectJExpressionPointcut는 AOP에 특화된 포인트컷 표현식을 적용할 수 있다.
'Spring' 카테고리의 다른 글
[Spring] 스프링 핵심 원리 - 고급편 섹션9,10 스프링 AOP 개념,구현 (0) | 2024.08.16 |
---|---|
[Spring] 스프링 핵심 원리 - 고급편 섹션8 @Aspect AOP (0) | 2024.08.16 |
[Spring] 스프링 핵심 원리 - 고급편 섹션6 스프링이 지원하는 프록시 (0) | 2024.08.04 |
[Spring] 스프링 핵심 원리 - 고급편 섹션5 동적 프록시 기술 (0) | 2024.08.04 |
[Spring] 스프링 핵심 원리 - 고급편 섹션4 프록시 패턴과 데코레이터 패턴 (0) | 2024.07.27 |