ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 어드바이저의 실제 적용
    스프링/스프링 AOP 2023. 9. 17. 18:01

    테스크 코드에서의 예제가 아니라 실제 적용하게 된다면 어떻게 동작하는지에 대한 포스팅이다.

     

     

    실제 사용하는 어드바이스 - 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);
    
                //로직 호출
                Object result = invocation.proceed();
    
                logTrace.end(status);
                return result;
            } catch (Exception e) {
                logTrace.exception(status, e);
                throw e;
            }
        }
    }

    Methodinterceptor를 상속받아서 invoke를 오버라이드하는 과정이다.

     

     

     

    main.class에 설정 추가

    @Import(ProxyFactoryConfigV1.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();
    	}
    
    }

     

    logTrace를 사용하고, Configuration을 proxyFactoryConfigV1만 사용하기 위해 메인 클래스에 특정 설정을 추가해준다.

     

     

     

     

    1. 인터페이스일 경우

    @Slf4j
    @Configuration
    public class ProxyFactoryConfigV1 {
    
        @Bean
        public OrderControllerV1 orderControllerV1(LogTrace logTrace) {
            OrderControllerV1 orderController = new OrderControllerV1Impl(orderServiceV1(logTrace));
            ProxyFactory factory = new ProxyFactory(orderController);
            factory.addAdvisor(getAdvisor(logTrace));
            OrderControllerV1 proxy = (OrderControllerV1) factory.getProxy();
            log.info("ProxyFactory proxy={}, target={}", proxy.getClass(), orderController.getClass());
            return proxy;
        }
    
        @Bean
        public OrderServiceV1 orderServiceV1(LogTrace logTrace) {
            OrderServiceV1 orderService = new OrderServiceV1Impl(orderRepositoryV1(logTrace));
            ProxyFactory factory = new ProxyFactory(orderService);
            factory.addAdvisor(getAdvisor(logTrace));
            OrderServiceV1 proxy = (OrderServiceV1) factory.getProxy();
            log.info("ProxyFactory proxy={}, target={}", proxy.getClass(), orderService.getClass());
            return proxy;
        }
    
        @Bean
        public OrderRepositoryV1 orderRepositoryV1(LogTrace logTrace) {
            OrderRepositoryV1Impl orderRepository = new OrderRepositoryV1Impl();
    
            ProxyFactory factory = new ProxyFactory(orderRepository);
            factory.addAdvisor(getAdvisor(logTrace));
            OrderRepositoryV1 proxy = (OrderRepositoryV1) factory.getProxy();
            log.info("ProxyFactory proxy={}, target={}", proxy.getClass(), orderRepository.getClass());
            return proxy;
        }
    
        private Advisor getAdvisor(LogTrace logTrace) {
            // pointcut
            NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
            pointcut.setMappedNames("request*", "order*", "save*");
    
            LogTraceAdvice advice = new LogTraceAdvice(logTrace);
            return new DefaultPointcutAdvisor(pointcut, advice);
        }
    }

    스프링 빈으로 설정하는 과정인데, 일단 pointcut을 설정한 뒤에 DefaultPointCutAdvisor를 반환하는 getAdvisor()를 선언한다. 그 뒤에 repository, service, controller 계층에서 의존관계 주입을 하며 프록시를 호출해준다. 코드가 길긴하지만 프록시 팩토리를 선언한 다음에 리턴해주는 것은 반복이니까 크게 추가된 점은 없다.

     

     

     

     

    2. 구체 클래스일 경우

    이 경우에도 코드는 위와 같다. 단지, V1 버전을 V2로 바꿔주면 된다. 그 이유는 저번 포스팅에 말했던 것처럼 스프링에서 지원하는 프록시 팩토리는 인터페이스의 여부에 따라 각각에 맞는 프록시를 동적으로 선택해주기 때문이다.

    이 경우에는 자동으로 CGLIB로 프록시가 생성되기 때문에 위의 코드에서 수정할 것이 없다.

     

     

     

    정리

    프록시 팩토리와 어드바이저, 어드바이스, 포인트컷이라는 개념 덕분에 어떤 부가 기능을 어디에 적용할 지 명확하게 적용할 수 있었다.

     

    여기에도 몇 가지 단점이 있는데, 설정이 너무 많다는 문제점과 컴포넌트 스캔의 단점이다.

     

    스프링 빈을 등록하는 것도 컴포넌트 스캔으로 하는데, configuration으로 빈을 등록하는 것도 모자라서 프록시를 적용하는 코드까지 빈 생성 코드에 넣어야한다.

     

    그리고 컴포넌트 스캔을 사용할 경우에는 이와 같은 프록시 적용이 불가능한데, 이러한 문제점을 해결해주는 것이 빈 후처리기이고, 다음 포스팅에서 설명할 예정이다.

    '스프링 > 스프링 AOP' 카테고리의 다른 글

    @Aspect 프록시  (0) 2023.09.20
    빈 후처리기  (0) 2023.09.17
    여러 어드바이저 동시 적용  (0) 2023.09.17
    포인트컷, 어드바이스, 어드바이저  (0) 2023.09.17
    프록시 팩토리(ProxyFactory)  (0) 2023.09.17
Designed by Tistory.