-
프록시를 이용한 로그추적기 - 구체클래스 기반스프링/스프링 AOP 2023. 9. 10. 23:06
일반적으로 인터페이스 기반 프록시가 더 좋다. 하지만 변경이 거의 일어나지 않는 클래스는 인터페이스를 굳이 만들 필요가 없기 때문에 구체클래스 기반 프록시를 사용하기도 한다.
OrderControllerConcreteProxy
public class OrderControllerConcreteProxy extends OrderControllerV2 { private final OrderControllerV2 target; private final LogTrace logTrace; public OrderControllerConcreteProxy(OrderControllerV2 target, LogTrace logTrace) { super(null); this.target = target; this.logTrace = logTrace; } @Override public String request(String itemId) { TraceStatus status = null; try { status = logTrace.begin("OrderController.request()"); String result = target.request(itemId); logTrace.end(status); return result; } catch (Exception e) { logTrace.exception(status, e); throw e; } } @Override public String noLog() { return target.noLog(); } }
생성자에 super(null)이라는 부분이 있다. controller의 생성자는 service가 들어가는데, controller를 상속받았으니 생성자에 넣어줘야 하는데, 프록시 기능만 사용하기 때문에 super(null)로 아무것도 넣어주지 않은 것이다.
인터페이스 기반 프록시와의 차이점은 구체클래스를 상속받는게 다르다는 것이다. 이 2가지를 제외하고는 이전 포스팅에서 여러번 반복하고 설명했던 내용이기 때문에 이번 포스팅에서는 코드의 내용보다 인터페이스 기반 프록시와 구체 기반 프록시의 차이점에 중점을 두고 설명하겠다.
OrderServiceConcreteProxy
public class OrderServiceConcreteProxy extends OrderServiceV2 { private final OrderServiceV2 target; private final LogTrace logTrace; public OrderServiceConcreteProxy(OrderServiceV2 target, LogTrace logTrace) { super(null); this.target = target; this.logTrace = logTrace; } @Override public void orderItem(String itemId) { TraceStatus status = null; try { status = logTrace.begin("OrderService.orderItem()"); target.orderItem(itemId); logTrace.end(status); } catch (Exception e) { logTrace.exception(status, e); throw e; } } }
OrderRepositoryConcreteProxy
@RequiredArgsConstructor public class OrderRepositoryConcreteProxy extends OrderRepositoryV2 { private final OrderRepositoryV2 target; private final LogTrace logTrace; @Override public void save(String itemId) { TraceStatus status = null; try { status = logTrace.begin("OrderRepository.request()"); target.save(itemId); logTrace.end(status); } catch (Exception e) { logTrace.exception(status, e); throw e; } } }
Configuration
@Configuration public class ConcreteProxyConfig { @Bean public OrderControllerV2 orderControllerV2(LogTrace logTrace) { OrderControllerV2 controllerImpl = new OrderControllerV2(orderServiceV2(logTrace)); return new OrderControllerConcreteProxy(controllerImpl, logTrace); } @Bean public OrderServiceV2 orderServiceV2(LogTrace logTrace) { OrderServiceV2 serviceImpl = new OrderServiceV2(orderRepositoryV2(logTrace)); return new OrderServiceConcreteProxy(serviceImpl, logTrace); } @Bean public OrderRepositoryV2 orderRepositoryV2(LogTrace logTrace) { OrderRepositoryV2 repositoryImpl = new OrderRepositoryV2(); return new OrderRepositoryConcreteProxy(repositoryImpl, logTrace); } }
여기서도 인터페이스 기반 프록시와 비슷하게 진행된다. 각각의 빈은 객체 클래스가 아닌 프록시 클래스를 반환하고 Controller, Service, Repository는 프록시 객체를 먼저 호출한 다음에 프록시 객체가 실제 객체를 호출하는 것이다.
인터페이스 기반 프록시가 더 좋다고 했지만 구현체가 거의 변경될 일이 없는 코드를 무작정 인터페이스화 시키는 것은 비효율적이다.
따라서 인터페이스 기반 프록시와 구체클래스 기반 프록시를 전부 다 알고있어야 한다.
단점
지금까지 프록시를 사용해서 기존 코드의 변경 없이 부가 기능을 적용했지만 프록시 클래스를 너무 많이 만들어야한다는 단점이 있다. LogTrace를 사용한다는 공통 특징이 있지만 대상 클래스가 다르기 때문에 여러 프록시 클래스를 만들어야 한다는 단점이 있다.
따라서 다음 포스팅인 '동적 프록시'에서 이 문제를 해결할 것이다.
'스프링 > 스프링 AOP' 카테고리의 다른 글
포인트컷, 어드바이스, 어드바이저 (0) 2023.09.17 프록시 팩토리(ProxyFactory) (0) 2023.09.17 프록시를 이용한 로그추적기 - 인터페이스 기반 (0) 2023.09.10 데코레이터 패턴 (0) 2023.09.10 프록시 패턴 (0) 2023.09.10