Proxy Factory

이창근·2023년 8월 22일
0

Spring공부

목록 보기
5/9

JDK Dynamic Proxy와 CGLIB 모두 환상적인 기술이지만, 가장 큰 문제는 JDK Dynamic Proxy는 인터페이스가 있는 클래스에만 적용할 수 있다는 점이다.
두 기술을 같이 사용하고 싶거나, 만약 스프링이 알아서 맞는 기술을 사용하여 동적 프록시를 생성해준다면 정말 편할 것이다. 이 부분에서 Proxy Factory가 등장한다.

Proxy Factory는 Advisor를 통해 작동하는데, Advisor는 Advice와 Pointcut이 합쳐진 것이다.

  • Advice : 적용할 부가 기능
  • Pointcut : 적용할 위치
  • Advisor : 위 둘을 합친 것

쉽게 설명하면 Proxy Factory는 Advisor를 받고, 이 Advisor의 정보를 통해 Pointcut이 지정한 위치에 Advice를 적용하는 것이다. 이때, 인터페이스가 있는지 없는지 확인하여 마법처럼 JDK Dynamic Proxy와 CGLIB기술을 선택하여 만들어준다.

Pointcut을 작성하는 방법이나, Configuration을 작성하는 방법은 여러 차이가 있으므로 먼저 Advice를 작성하는 방법부터 소개해보겠다.

LogTraceAdvice.class

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() 함수를 오버라이딩하면 된다. 그 세부내용은 이전의 수많은 프록시들과 닮아있다. 핵심은 이를 어떻게 적용하느냐 인데, 가장 손쉬운 방법인 자동 프록시 생성기를 사용해보고자 한다.
자동 프록시 생성기는 빈 후처리기의 일종으로, 스프링에서 제공하는 막강한 빈 후처리기이다. 아래 과정을 통해 작동하게 된다.

  1. 스프링이 스프링 빈 대상의 모든 객체를 생성한다.
  2. 생성된 객체를 빈 후처리기에 전달한다.
  3. 스프링 컨테이너에서 모든 Advisor 빈을 조회한다.
  4. 해당 생성된 객체를 Advisor의 Pointcut을 통해 프록시 적용 대상인지 체크한다. 만약 하나라도 적용 대상이 된다면 프록시 객체가 되야하고, 모두 매칭되지 않는 경우에만 그대로 넘긴다.
  5. 프록시 적용 대상이 된 객체에 대해 프록시를 생성하고 스프링 빈에 등록한다. 4번 과정에서 그대로 넘겨진 객체는 그대로 스프링 빈에 등록한다.

AOP, AspectJ에 대한 내용도 알아야 잘 이해할 수 있지만 일단 사용하는 방법을 알아보겠다.

build.gradle

implementation 'org.springframework.boot:spring-boot-starter-aop'

먼저 위 라이브러리를 추가하여 aspectJ관련 라이브러리를 등록한다. 그 후에는 Configuration을 작성한다.

AutoProxyConfig.class

@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에 대해 좀 더 자세하게 다뤄보겠다.

profile
나중에 또 모를 것들 모음

0개의 댓글