직접 @Configuration을 이용해서 수동으로 빈을 등록하는 방법이다.
@Bean
public OrderRepository orderRepository(LogTrace logTrace){
OrderRepository orderRepository = new OrderRepository();
ProxyFactory factory = new ProxyFactory(orderRepository);
factory.addAdvisor(getAdvisor(logTrace));
OrderRepository proxy = (OrderRepository) factory.getProxy();
return proxy;
}
private Advisor getAdvisor(LogTrace logTrace) {
//pointcut
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("request*", "order*", "save*");
LogTraceAdvice logTraceAdvice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, logTraceAdvice);
}
빈으로 직접 ProxyFactory를 실제 객체를 프록시 객체로 바꾸고 반환한다.
이 경우에는 실제 객체를 바로 주입하는 @ComponentScan을 실제 객체에 달 수 없다.
이를 포함하는 @Repository, @Service, @Controller도 당연히 불가능하다.
프록시 객체를 생성해주기 위해서 너무 많은 설정 작업이 필요해서 나온 방법이 빈 후처리기가 있다.
스프링의 빈 등록 과정은
1. 스프링이 빈 대상이 되는 객체를 생성하고
2. 생성된 객체를 빈 저장소에 등록하기 직전에 빈 후처리기에 전달한다.
3. 빈 후처리기는 객체를 조작하거나 객체를 바꿔칠 수도 있다. 이후 객체를 반환한다.
4. 반환된 객체는 빈 저장소에 등록된다.
// BeanPostProcessor는 인터페이스로 PostConstructor 이후 혹은 이전에 조작할지 정할 수 있다.
// BeanPostProcessor를 상속받는 클래스를 Bean으로 등록만 해주면 된다.
@Test
public void beanPostConfig() throws Exception{
// 스프링 컨테이너
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(BeanPostConfigProcessor.class);
//B는 빈으로 등록된다.
B b = applicationContext.getBean("beanA", B.class);
b.helloB();
//A는 빈으로 등록되지 않는다.
Assertions.assertThrows(NoSuchBeanDefinitionException.class,
() -> applicationContext.getBean(A.class));
}
@Slf4j
@Configuration
static class BeanPostConfigProcessor {
@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;
}
}
@PostConstructor는 스프링 빈 생성 이후에 빈을 초기화하는 역할을 한다.
메소드에 어노테이션만 붙여줘도 빈 생성 이후 한 번더 조작을 해주는데, 이 말은
스프링이 기본적으로 호출하는 빈 후처리기가 있다는 것이다.
스프링은 CommonAnootationBeanPostProccessor라는 빈 후처리기를 자동으로 등록하는데
여기서 @PostConstructor 어노테이션이 붙은 메소드를 자동으로 호출한다.
스프링 스타터 aop 라이브러리를 추가하면 AopAutoConfiguration에서 AOP와 관련된 클래스들을 bean으로 추가한다. 여기서 AnnotationAspectJAutoProxyCreator라는 빈 후처리기가 스프링 빈에 자동으로 등록되는데, 스프링 빈으로 등록된 Advisor를 찾아서 프록시가 필요한 곳에 자동으로 프록시를 적용해준다. 추가로 @AspectJ와 관련된 AOP 기능도 찾아서 처리해준다.