Search
Duplicate
📒

[Spring Study] 08-5. 빈 후처리기

상태
완료
수업
Spring Study
주제
AOP
연관 노트
3 more properties
참고

Bean 후처리기(BeanPostProcessor)

NOTE
스프링 Bean 후처리기를 사용하면 스프링 컨테이너에 Bean을 등록하기 직전에 Bean을 조작하거나, 완전히 다른 객체로 바꿔칠 수 있습니다.
Bean 후처리기의 주요 기능은 다음과 같습니다.
1.
Bean 조작: 생성된 Bean의 속성을 변경하거나 특정 메소드를 호출할 수 있습니다.
2.
객체 바꿔치기: 생성된 Bean 대신에 완전히 다른 객체를 반환하여 스프링 컨테이너에 등록할 수 있습니다.
스프링은 CommonAnnotationBeanPostProcessor라는 Bean 후처리기를 자동으로 등록하며, 이를 통해서 스프링 생명주기 애노테이션을 지원합니다.
ex) @PostConstruct, @PreDestroy, @Resource
Bean 후처리기를 테스트 하기 위해 A 객체B 객체로 바꿔치는 예제코드를 작성해보겠습니다.
@Slf4j public class A { public void helloA() { log.info("hello A"); } } @Slf4j public class B { public void helloB() { log.info("hello B"); } }
Java
복사
테스트 객체
@Test void beanPostProcessorTest(){ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BasicConfig.class); // A는 Bean으로 등록된다. A beanA = applicationContext.getBean("beanA", A.class); // B는 Bean으로 등록되지 않는다. Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean(B.class)); } @Slf4j @Configuration static class BasicConfig { // A만 등록한다. @Bean(name = "beanA") public A a() { return new A(); } }
Java
복사
정상 흐름 (A 등록, 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; } }
Java
복사
빈후처리기 추가
@Test void beanPostProcessorTest(){ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BasicConfig.class); // B는 Bean으로 등록된다. B beanB = applicationContext.getBean("beanA", B.class); // A는 Bean으로 등록되지 않는다. (바꿔치기됨) Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean(A.class)); } @Slf4j @Configuration static class BasicConfig { @Bean(name = "beanA") public A a() { return new A(); } @Bean public AToBPostProcessor helloPostProcessor() { return new AToBPostProcessor(); } }
Java
복사
A → B 변환 성공!

Bean 후처리기 프록시 적용

NOTE
스프링 Bean 후처리기를 사용해 실제 객체 대신 프록시 객체를 스프링 빈으로 등록해봅시다!
빈 후처리기 프로세스
@Slf4j static class PackageLogTraceProxyPostProcessor implements BeanPostProcessor { private final String basePackage; private final Advisor advisor; public PackageLogTraceProxyPostProcessor(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; } }
Java
복사
빈 후처리기 (프록시 적용)
PackageLogTraceProxyPostProcessor는 특정 패키지 내의 Bean들에 대해 프록시를 적용합니다. 이때 프록시 팩토리를 사용하여 Advisor를 적용합니다.
현재는 A,B는 프록시가 적용되며, Pointcut은 A에만 적용되도록 설정한다.
@Slf4j @Configuration static class BasicConfig { @Bean(name = "beanA") public A a() { return new A(); } @Bean(name = "beanB") public B b() { return new B(); } @Bean public PackageLogTraceProxyPostProcessor packageLogTraceProxyPostProcessor() { return new PackageLogTraceProxyPostProcessor("com.example.study.aop", getAdvisor()); } private Advisor getAdvisor() { AspectJExpressionPointcut aspectJPointcut = new AspectJExpressionPointcut(); aspectJPointcut.setExpression("within(com.example.study.aop.BeanPostProcessorTest.A)"); return new DefaultPointcutAdvisor(aspectJPointcut, new TimeAdvice()); } }
Java
복사
테스트 config
@Test void beanPostProcessorTest2(){ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BasicConfig.class); // A는 프록시로 등록된다. A proxyA = applicationContext.getBean(A.class); proxyA.helloA(); // B는 프록시로 등록되지 않는다. B beanB = applicationContext.getBean(B.class); beanB.helloB(); }
Java
복사
테스트 코드

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

NOTE
스프링 부트는 자동 설정으로 AnnotationAwareAspectAutoProxyCreator라는 Bean 후처리기가 스프링 빈에 자동으로 등록됩니다. 이 Bean 후처리기는 자동으로 Proxy를 생성해주는 역할을 해줍니다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
Groovy
복사
gradle에 추가
위로 계속 올라가면 BeanProcessor를 구현받고 있는걸 확인할 수 있다.

자동 프록시 생성기 - AutoProxyCreator

NOTE
프록시 흐름
@Slf4j @Configuration @EnableAspectJAutoProxy // 실제 스프링부트에서는 필요없음(자동등록) static class BasicConfig { @Bean(name = "beanA") public A a() { return new A(); } @Bean(name = "beanB") public B b() { return new B(); } // Advisor 빈등록(자동 빈후처리기가 찾아서 등록함) // A 클래스 등록 @Bean public Advisor timeAdvisor(){ AspectJExpressionPointcut aspectJPointcut = new AspectJExpressionPointcut(); aspectJPointcut.setExpression("within(com.example.study.aop.BeanPostProcessorTest.A)"); return new DefaultPointcutAdvisor(aspectJPointcut, new TimeAdvice()); } }
Java
복사
Tset Config
@Test void autoProxyTest(){ ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BasicConfig.class); // A는 프록시로 등록된다. A proxyA = applicationContext.getBean(A.class); proxyA.helloA(); // B는 프록시로 등록되지 않는다. B beanB = applicationContext.getBean(B.class); beanB.helloB(); }
Java
복사
Test Code
빈 후처리기를 등록하지 않아도, 자동으로 등록된 빈 후처리기가 A를 프록시로 만들어주었습니다.