오봉이와 함께하는 개발 블로그
스프링 핵심 원리 - 고급편 > 빈 후처리기 예제 본문
스프링 핵심 원리 - 고급편 > 빈 후처리기 예제1
빈 후처리기를 하기 전에 일반적인 스프링 빈 등록 과정을 코드로 작성해보자.
@Slf4j
public class BasicTest {
@Test
void basicConfig() {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BasicConfig.class);
A a = applicationContext.getBean("beanA", A.class);
a.helloA();
Assertions.assertThrows(
NoSuchBeanDefinitionException.class, () -> applicationContext.getBean("beanB", A.class));
}
@Configuration
static class BasicConfig {
@Bean(name = "beanA")
public A a() {
return new A();
}
}
static class A {
public void helloA() {
log.info("hello A");
}
}
static class B {
public void helloB() {
log.info("hello B");
}
}
}
간단하게 스프링 컨테이너를 생성하면서 BasicConfig.class를 넘겨주었고, BasicConfig.class은 스프링 빈으로 등록된다.
BasicConfig를 통해 A 클래스를 Bean으로 등록했고, B는 등록한 적 없기 때문에 NoSuchBeanDefinitionException가 발생하는 테스트 코드가 성공하게 된다.
스프링 핵심 원리 - 고급편 > 빈 후처리기 예제2
이번에는 빈 후처리기를 통해 A를 B 객체로 바꿔치기 해보자.
BeanPostProcessor 인터페이스 - 스프링 제공
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
- 빈 후처리기를 사용하려면
BeanPostProcessor
인터페이스를 구현하고, 스프링 빈으로 등록하면 된다. - postProcessBeforeInitialization
- 객체 생성 이후
@PostConstruct
같은 초기화가 발생하기 전에 호출되는 포스트 프로세서이다.
- 객체 생성 이후
- postProcessAfterInitialization
- 객체 생성 이후에
@PostConstruct
같은 초기화가 발생한 다음에 호출되는 포스트 프로세서이다.
- 객체 생성 이후에
@PostConstruct
같은 것은 스프링 빈 생명주기를 참고하자.
@Slf4j
public class BeanPostProcessorTest {
@Test
void postProcessor() {
ApplicationContext ap = new AnnotationConfigApplicationContext(BeanPostProcessorConfig.class);
B b = ap.getBean("beanA", B.class);
b.helloB();
Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> ap.getBean(A.class));
}
@Configuration
static class BeanPostProcessorConfig {
@Bean(name = "beanA")
public A a() {
return new A();
}
@Bean
public AtoBPostProcessor helloPostProcessor() {
return new AtoBPostProcessor();
}
}
static class A {
public void helloA() {
log.info("hello A");
}
}
static class B {
public void helloB() {
log.info("hello B");
}
}
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;
}
}
}
위 코드의 실행 결과는 아래 로그를 통해 확인할 수 있다.
실행 결과를 보면 최종적으로 beanA
라는 스프링 빈 이름에 A 객체 대신 B 객체가 등록된 것을 확인할 수 있다.
A 객체는 스프링 빈으로 등록조차 되지 않는다.
간단하게 직접 찍은 로그만 확인해보면..
beanName=beanA bean=hello.proxy.postprocessor.BeanPostProcessorTest$A@3ed242a4
hello B
이런 로그가 찍히는데, 만약 postProcessor를 적용해놓고 A로 받으면 어떻게 될까?
@Test
void postProcessor() {
ApplicationContext ap = new AnnotationConfigApplicationContext(BeanPostProcessorConfig.class);
A a = ap.getBean("beanA", A.class);
a.helloA();
}
beanName=beanA bean=hello.proxy.postprocessor.BeanPostProcessorTest$A@3ed242a4
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'beanA' is expected to be of type 'hello.proxy.postprocessor.BeanPostProcessorTest$A' but was actually of type 'hello.proxy.postprocessor.BeanPostProcessorTest$B'
위와 같은 에러 로그가 발생한다.
코드를 천천히 뜯어보자면
AtoBPostProcessor
- 빈 후처리기다.
- 인터페이스인 BeanPostProcessor를 구현하고, 스프링 빈으로 등록하면 스프링 컨테이너가 빈 후처리기로 인식하고 동작한다.
- 이 빈 후처리기는 A 객체를 새로운 B 객체로 바꿔치기 한다.
- 파라미터로 넘어오은 빈(bean) 객체가 A의 인스턴스면 새로운 B 객체를 생성하여 반환한다.
- 여기서 A 대신 반환된 값인 B가 스프링 컨테이너에 등록된다.
- beanName=beanA bean=hello.proxy.postprocessor.BeanPostProcessorTest$A@3ed242a4
- 위 로그를 보면 빈 후처리기에 A가 bean 이라는 파라미터로 넘어온 것을 확인할 수 있다.
정리
빈 후처리기는 빈을 조작하고 변경할 수 있는 후킹 포인트다.
빈 객체를 조작하거나 심지어 다른 객체로 바꿀 수 있을 정도로 강력한 기능이다.
여기서 조작이라는 것은 해당 객체의 특정 메서드를 호출하는 것을 뜻한다.
일반적으로 스프링 컨테이너가 등록하는, 특히 컴포넌트 스캔의 대상이 되는 빈들은 중간에 조작할 방법이 없는데, 빈 후처리기를 사용하면 개발자가 등록하는 모든 빈을 중간에 조작할 수 있다.
이 말은 빈 객체를 프록시로 교체하는 것도 가능하다는 뜻이다.
참고: @PostConstruct의 비밀
@PostConstruct
는 스프링 빈 생성 이후에 빈을 초기화 하는 역할을 한다.
그런데 생각해보면 빈의 초기화 라는 것이 단순히 @PostConstruct
애노테이션이 붙은 초기화 메서드를 한번 호출만 하면 된다. 쉽게 이야기해 서 생성된 빈을 한번 조작하는 것이다.
따라서 빈을 조작하는 행위를 하는 적절한 빈 후처리기가 있으면 될 것 같다.
스프링은 CommonAnnotationBeanPostProcessor
라는 빈 후처리기를 자동으로 등록하는데, 여기에서 @PostConstruct
애노테이션이 붙은 메서드를 호출한다.
따라서 스프링 스스로도 스프링 내부의 기능을 확장 하기 위해 빈 후처리기를 사용한다.
출처: 김영한 지식공유자의 스프링 핵심 원리 고급편
'BE > Spring' 카테고리의 다른 글
스프링 핵심 원리 고급편 - 스프링이 제공하는 빈 후처리기 1, 2 (0) | 2024.10.08 |
---|---|
스프링 핵심 원리 - 고급편 > 빈 후처리기 적용, 정리 (0) | 2024.10.07 |
스프링 핵심 원리 - 고급편 > 빈 후처리기 소개 (0) | 2024.10.06 |
스프링 핵심 원리 - 고급편 > 프록시 팩토리 적용 1, 2, 정리 (0) | 2024.10.03 |
스프링 핵심 원리 - 고급편 > 여러 어드바이저 함께 적용 (0) | 2024.10.03 |