JDK Dynamic Proxy와 CGLIB 모두 환상적인 기술이지만, 가장 큰 문제는 JDK Dynamic Proxy는 인터페이스가 있는 클래스에만 적용할 수 있다는 점이다.
두 기술을 같이 사용하고 싶거나, 만약 스프링이 알아서 맞는 기술을 사용하여 동적 프록시를 생성해준다면 정말 편할 것이다. 이 부분에서 Proxy Factory가 등장한다.
Proxy Factory는 Advisor를 통해 작동하는데, Advisor는 Advice와 Pointcut이 합쳐진 것이다.
쉽게 설명하면 Proxy Factory는 Advisor를 받고, 이 Advisor의 정보를 통해 Pointcut이 지정한 위치에 Advice를 적용하는 것이다. 이때, 인터페이스가 있는지 없는지 확인하여 마법처럼 JDK Dynamic Proxy와 CGLIB기술을 선택하여 만들어준다.
Pointcut을 작성하는 방법이나, Configuration을 작성하는 방법은 여러 차이가 있으므로 먼저 Advice를 작성하는 방법부터 소개해보겠다.
public class LogTraceAdvice implements MethodInterceptor {
private final LogTrace logTrace;
public LogTraceAdvice(LogTrace logTrace) {
this.logTrace = logTrace;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
TraceStatus status = null;
try {
Method method = invocation.getMethod();
String message = method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()";
status = logTrace.begin(message);
//target호출
Object result = invocation.proceed();
logTrace.end(status);
return result;
} catch (Exception e) {
logTrace.exception(status, e);
throw e;
}
}
}
Advice는 위와 같이 MethodInterceptor 인터페이스를 구현하여 만들고, invoke() 함수를 오버라이딩하면 된다. 그 세부내용은 이전의 수많은 프록시들과 닮아있다. 핵심은 이를 어떻게 적용하느냐 인데, 가장 손쉬운 방법인 자동 프록시 생성기를 사용해보고자 한다.
자동 프록시 생성기는 빈 후처리기의 일종으로, 스프링에서 제공하는 막강한 빈 후처리기이다. 아래 과정을 통해 작동하게 된다.
AOP, AspectJ에 대한 내용도 알아야 잘 이해할 수 있지만 일단 사용하는 방법을 알아보겠다.
implementation 'org.springframework.boot:spring-boot-starter-aop'
먼저 위 라이브러리를 추가하여 aspectJ관련 라이브러리를 등록한다. 그 후에는 Configuration을 작성한다.
@Configuration
@Import({AppV1Config.class, AppV2Config.class})
public class AutoProxyConfig {
@Bean
public Advisor advisor(LogTrace logTrace) {
//pointcut
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.proxy.app..*(..)) && !execution(* hello.proxy.app..noLog(..))");
//advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
return new DefaultPointcutAdvisor(pointcut, advice);
}
}
위 Configuration을 위에서부터 설명하면,
//pointcut
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* hello.proxy.app..*(..)) && !execution(* hello.proxy.app..noLog(..))");
이 부분은 Pointcut을 설정하는 부분이다. 생소한 aspectJ 포인트컷 표현식이 생성되었으나, 관련 문서를 읽으면 쉽게 사용할 수 있을 것이다.
//advice
LogTraceAdvice advice = new LogTraceAdvice(logTrace);
또한 이 부분은 위에서 만든 LogTraceAdvice.class를 통해 advice를 생성하는 부분이다.
이렇게 Advisor를 리턴하는 생성자를 Bean에 등록해주기만 하면 나머지는 자동 프록시 생성기가 위에 설명한 순서대로 프록시를 만들어주게 된다.
이제 인터페이스를 사용하는 클래스든, 인터페이스를 사용하지 않는 구체 클래스든 가리지 않고 모두 원하는 위치에 프록시를 적용할 수 있게 되었다.
이후에는 @Aspect 어노테이션과 스프링 AOP에 대해 좀 더 자세하게 다뤄보겠다.