BeanPostProcessor 를 이용한 프록시 객체 생성

목차

참고

본 포스트는 김영한의 스프링 핵심 원리 - 고급편 내용을 참고해 만들었습니다.

BeanPostProcessor 를 이용한 프록시 객체 생성

빈 후처리기를 이용해 Bean 생성시점에 프록시 객체를 생성할 수 있습니다. 다만, 스프링에 등록되는 모든 빈들은 빈 후처리기로 넘어옵니다. 그래서 모든 Bean 에 대해 BeanPostProcessor 로직이 수행되므로 어떤 Bean 을 Proxy Bean 으로 생성할지에 대한 기준이 필요하다.

BeanPostProcessor 를 이용한 프록시 객체 생성 과정

빈 후처리기를 이용해 프록시 객체를 생성하는 방법은 4가지 단계가 있습니다.

첫번째는 Bean 대상이 되는 객체를 생성합니다. 두번째는 생성된 객체를 빈 후처리기에 전달합니다. 셋번째는 빈 후처리기에서 전달 받은 객체를 이용해 프록시 객체를 생성 후 반환합니다. 네번째 빈 후처리기에서 반환된 프록시 객체를 Bean 저장소에 등록합니다.

빈 후처리기를 이용한 프록시 객체 생성

프록시 객체 생성 예 - PackageLogTracePostProcessor 생성

PackageLogTracePostProcessor 는 넘어온 Bean 의 Package 를 확인 후 Proxy 객체 를 생성합니다. Package 이름이 설정된 basePackage 로 시작하면 Proxy 객체를 반환하고 아닐 경우에는 그대로를 다시 반환한다.

@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;
}
}

BeanPostProcessor 등록

  • logTracePostProcessor
    • 특정 패키지를 기준으로 Proxy 를 생성하는 BeanProcessor 를 Spring Bean 으로 등록 한다.
    • BeanProcessor 를 Bean 으로 등록하면 자동으로 동작한다.
@Slf4j
@Configuration
@Import({AppV1Config.class, AppV2Config.class})
public class BeanPostProcessorConfig {

@Bean
public PackageLogTracePostProcessor logTracePostProcessor(LogTrace logTrace){
// hello.proxy.app 패키지에 속한 클래스만 적용된다.
return new PackageLogTracePostProcessor("hello.proxy.app", getAdvisor(logTrace));
}

private Advisor getAdvisor(LogTrace logTrace) {
// Point Cut
NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
pointcut.setMappedNames("request*", "order*", "save*");

// advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);

// Pointcut 과 Advice 를 이용해 Advisor 객체를 생성한다.
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();
}
}
Share