빈 후처리기
스프링이 빈 저장소에 등록할 목적으로 생성한 객체를 빈 저장소에 등록하기 직전에 조작할 때 사용한다.
이름 그대로 빈을 생성한 후에 무언가를 처리하는 용도로 사용한다
기능
- 객체 조작
- 객체 바꿔치기
구현
BeanPostProcessor을 구현하고 빈으로 등록하면 된다.
다음은 beanA를 등록했지만 beanB로 바꿔치기 하는 예제 코드이다.
분명 A클래스의 빈 이름이 beanA로 등록되어 있지만 결과는 getBean을 통해 beanA를 조회하면 B로 반환되고, A 클래스의 빈은 조회되지 않음을 확인할 수 있다.
public class BeanPostProcessorTest {
@Test
void postProcessor() {
ApplicationContext applicationContext = new
AnnotationConfigApplicationContext(BeanPostProcessorConfig.class);
//beanA 이름으로 B 객체가 빈으로 등록된다.
B b = applicationContext.getBean("beanA", B.class);
b.helloB();
//A는 빈으로 등록되지 않는다.
Assertions.assertThrows(NoSuchBeanDefinitionException.class,
() -> applicationContext.getBean(A.class));
}
@Slf4j
@Configuration
static class BeanPostProcessorConfig {
@Bean(name = "beanA")
public A a() {
return new A();
}
@Bean
public AToBPostProcessor helloPostProcessor() {
return new AToBPostProcessor();
}
}
@Slf4j
static class A {
public void helloA() {
log.info("hello A");
}
}
@Slf4j
static class B {
public void helloB() {
log.info("hello B");
}
}
@Slf4j
static class AToBPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String
beanName) throws BeansException {
log.info("beanName={} bean={}", beanName, bean);
if (bean instanceof A) {
return new B();
}
return bean;
}
}
}
적용
빈 후처리기를 사용하면 실제 객체에 대한 빈을 프록시 빈으로 바꿔치기 할 수 있다.
스프링 부트에서는 자동으로 AnnotationAwareAspectJAutoProxyCreator 라는 빈 후처리기가 스프링 빈에 자동으로 등록된다.
이 빈 후처리기는 스프링 빈으로 등록된 Advisor 들을 자동으로 찾아서 프록시가 필요한 곳에 자동으로 프록시를 적용해준다
Advisor 는 물론이고, @Aspect와 같은 AOP 기능도 자동으로 인식해서 프록시를 만들고 AOP를 적용해준다.
그렇다면 어드바이저 advisor란 무엇인가?
어드바이저
어드바이저란 포인트컷 + 어드바이스를 말한다.
포인트컷: 어디에 부가 기능을 적용할지 판단하는 필터링 로직
어드바이스: 프록시가 호출하는 부가 기능, 프록시 로직
구현
Pointcut을 구현하여 직접 생성할 수 있지만 스프링은 우리가 필요한 대부분의 포인트컷을 제공한다.
NameMathMethodPointcut
@Test
@DisplayName("스프링이 제공하는 포인트컷")
void advisorTest3() {
ServiceImpl target = new ServiceImpl();
ProxyFactory proxyFactory = new ProxyFactory(target);
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("save");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,
new TimeAdvice());
proxyFactory.addAdvisor(advisor);
ServiceInterface proxy = (ServiceInterface) proxyFactory.getProxy();
proxy.save();
proxy.find();
}
setMappedName를 통해 메소드 이름이 save인 메소드만 어드바이스를 적용한다. 해당 메소드는 내부에서 PatternMatchUtils를 사용한다.
앞선 포스팅에서 설명했듯이 기본적으로 프록시 팩토리를 통해 어드바이저를 추가하여 실행할 수 있다.
그렇다면 여러 어드바이스(추가 기능)을 적용하려면 어떻게 해야 할까?
여러 어드바이저
프록시 팩토리는 여러 어드바이저를 등록하여 여러 개의 추가 기능을 덧붙여 사용할 수 있다.
등록하는 순서대로 어드바이저가 실행된다.
ProxyFactory proxyFactory = new ProxyFactory(target);
proxyFactory.addAdvisor(advisor2);
proxyFactory.addAdvisor(advisor1);
이 말은 즉, 하나의 프록시가 여러 개의 어드바이저를 가질 수 있다는 뜻이다. 여러 어드바이저를 적용한다고 해서 여러 개의 프록시가 생성되는 것이 아니다.
그래서 빈 후처리기랑 어드바이저가 어떤 관계고 어떻게 작동하는 건지 정리가 안 될 것이다. 순서대로 정리를 해보자.
정리
먼저 비즈니스 로직을 변경없이 추가 적인 기능을 적용하기 위해서 실제 객체가 아닌 프록시 객체로 만들어 부가 기능을 부여해주었다.
이때 부가 기능이 어드바이스, 부가 기능이 적용되는 클래스와 메소드를 필터링 하는 것이 포인트컷이다.
그렇다면 부가 기능을 적용시킬 모든 객체에 대해 프록시로 변환해주어야 하나?
이를 해결해주기 위해 빈 후처리기가 적용되어 실제 객체에 대한 빈을 프록시 빈으로 바꿔치기 하게 된다.
스프링은 AnnotationAwareAspectJAutoProxyCreator라는 자동 빈 생성기를 통해 스프링 빈으로 등록된 Advisor를 찾고 포인트컷을 통해 적용 대상을 체크 후 해당하는 객체에 대해 프록시로 바꿔치기 해준다.
즉 advisor만 구현해서 스프링 빈으로 등록해주면 편리하게 프록시 기능을 사용할 수 있다는 것이다.
하지만 앞서 봤듯이 advisor를 구현하는 것이 다소 복잡하고 번거로울 수 있다. 그래서 스프링은 @Aspect 어노테이션을 통해 더욱 편리하게 advisor(pointcut + advice)를 구현하게 도와준다.
'Study > Spring' 카테고리의 다른 글
스프링 AOP의 문제점 및 한계 (0) | 2023.10.14 |
---|---|
AOP (Aspect - Oriented Programming)와 @Aspect (0) | 2023.10.12 |
리플렉션과 프록시 팩토리 (0) | 2023.10.11 |
PRG Post/Redirect/Get (0) | 2023.08.22 |
HTTP 헤더, 파라미터, 바디 조회하는 방법 (0) | 2023.08.21 |