728x90
InvocationHandler
@Slf4j
public class LogTraceBasicHandler implements InvocationHandler {
private final Object target;
private final LogTrace logTrace;
public LogTraceBasicHandler(Object target, LogTrace logTrace) {
this.target = target;
this.logTrace = logTrace;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
TraceStatus status = null;
try {
String message = method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()";
status = logTrace.begin(message);
//target 호출
Object result = method.invoke(target, args);
logTrace.end(status);
return result;
}catch (Exception e){
logTrace.exception(status, e);
throw e;
}
}
}
- LogTraceBasicHandler는 InvocationHandler 인터페이스를 구현해서 JDK 동적 프록시에서 사용된다
- private final Object target : 프록시가 호출할 대상이다.
- String message = method.getDeclaringClass().getSimpleName() + "." ...
- LogTrace에 사용할 메시지이다. 프록시를 직접 개발할 때는 "OrderController.request()" 와 같이 프록시마다 호출되는 클래스와 메서드 이름을 직접 남겼다.
- 이제는 Method 를 통해서 호출되는 메서드 정보와 클래스 정보를 동적으로 확인할 수 있기 때문에 이 정보를 사용하면 된다.
빈 등록
@Configuration
public class DynamicProxyBasicConfig {
@Bean
public OrderRepositoryV1 orderRepositoryV1(LogTrace logTrace) {
OrderRepositoryV1Impl orderRepository = new OrderRepositoryV1Impl();
OrderRepositoryV1 proxy = (OrderRepositoryV1) Proxy
.newProxyInstance(OrderRepositoryV1.class.getClassLoader(),
new Class[]{OrderRepositoryV1.class},
new LogTraceBasicHandler(orderRepository, logTrace));
return proxy;
}
@Bean
public OrderServiceV1 orderServiceV1(LogTrace logTrace) {
OrderServiceV1Impl orderService = new OrderServiceV1Impl(orderRepositoryV1(logTrace));
OrderServiceV1 proxy = (OrderServiceV1) Proxy
.newProxyInstance(OrderServiceV1.class.getClassLoader(),
new Class[]{OrderServiceV1.class},
new LogTraceBasicHandler(orderService, logTrace));
return proxy;
}
@Bean
public OrderControllerV1 orderControllerV1(LogTrace logTrace) {
OrderControllerV1 orderController = new OrderControllerV1Impl(orderServiceV1(logTrace));
OrderControllerV1 proxy = (OrderControllerV1) Proxy
.newProxyInstance(OrderControllerV1.class.getClassLoader(),
new Class[]{OrderControllerV1.class},
new LogTraceBasicHandler(orderController, logTrace));
return proxy;
}
}
- 이전에는 프록시 클래스를 직접 개발했지만, 이제는 JDK 동적 프록시 기술을 사용해서 각각의 Controller , Service , Repository에 맞는 동적 프록시를 생성해주면 된다.
- LogTraceBasicHandler : 동적 프록시를 만들더라도 LogTrace를 출력하는 로직은 모두 같기 때문에 프록시는 모두 LogTraceBasicHandler를 사용한다




ProxyApplication 수정
@Import(DynamicProxyBasicConfig.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();
}
}
- 동적 프록시를 빈으로 등록한 설정 파일을 Import 한다.
- no-log에도 동적 프록시가 적용이 되어 로그가 남겨진다(no-log는 로그 남기면 X)
메서드 이름 필터 기능 추가
Pattern 에 Match 되는 것만 실행하도록 코드 수정
@Slf4j
public class LogTraceFilterHandler implements InvocationHandler {
private final Object target;
private final LogTrace logTrace;
private final String[] patterns; //해당 패턴일떄만 로그를 남긴다.
public LogTraceFilterHandler(Object target, LogTrace logTrace, String[] patterns) {
this.target = target;
this.logTrace = logTrace;
this.patterns = patterns;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//메서드 이름 필터
String methodName = method.getName();
//save.request, reque*, *est
if(!PatternMatchUtils.simpleMatch(patterns, methodName)){
return method.invoke(target, args); //실제 호출
}
TraceStatus status = null;
try {
String message = method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()";
status = logTrace.begin(message);
//target 호출
Object result = method.invoke(target, args);
logTrace.end(status);
return result;
}catch (Exception e){
logTrace.exception(status, e);
throw e;
}
}
}
- logTraceFilterHandler는 기존 기능에 다음 기능이 추가되었다. 특정 메서드 이름이 매칭 되는 경우에만 LogTrace 로직을 실행한다. 이름이 매칭 되지 않으면 실제 로직을 바로 호출한다
- 스프링이 제공하는 PatternMatchUtils.simpleMatch(..) 를 사용하면 단순한 매칭 로직을 쉽게 적용할 수 있다.
- xxx : xxx가 정확히 매칭되면 참
- xxx* : xxx로 시작하면 참
- *xxx : xxx로 끝나면 참
- *xxx* : xxx가 있으면 참
- String [] patterns : 적용할 패턴은 생성자를 통해서 외부에서 받는다.
빈 등록시 패턴 추가
@Configuration
public class DynamicProxyFilterConfig {
private static final String[] PATTERNS = {"request*", "order*", "save*"};
@Bean
public OrderRepositoryV1 orderRepositoryV1(LogTrace logTrace) {
OrderRepositoryV1Impl orderRepository = new OrderRepositoryV1Impl();
OrderRepositoryV1 proxy = (OrderRepositoryV1) Proxy
.newProxyInstance(OrderRepositoryV1.class.getClassLoader(),
new Class[]{OrderRepositoryV1.class},
new LogTraceFilterHandler(orderRepository, logTrace, PATTERNS));
return proxy;
}
@Bean
public OrderServiceV1 orderServiceV1(LogTrace logTrace) {
OrderServiceV1Impl orderService = new OrderServiceV1Impl(orderRepositoryV1(logTrace));
OrderServiceV1 proxy = (OrderServiceV1) Proxy
.newProxyInstance(OrderServiceV1.class.getClassLoader(),
new Class[]{OrderServiceV1.class},
new LogTraceFilterHandler(orderService, logTrace, PATTERNS));
return proxy;
}
@Bean
public OrderControllerV1 orderControllerV1(LogTrace logTrace) {
OrderControllerV1 orderController = new OrderControllerV1Impl(orderServiceV1(logTrace));
OrderControllerV1 proxy = (OrderControllerV1) Proxy
.newProxyInstance(OrderControllerV1.class.getClassLoader(),
new Class[]{OrderControllerV1.class},
new LogTraceFilterHandler(orderController, logTrace, PATTERNS));
return proxy;
}
}
- public static final String [] PATTERNS = {"request*", "order*", "save*"};
- 적용할 패턴이다. request , order , save로 시작하는 메서드에 로그가 남는다.
- LogTraceFilterHandler : 앞서 만든 필터 기능이 있는 핸들러를 사용한다. 그리고 핸들러에 적용 패턴도 넣어준다.
ProxyApplication 수정
@Import(DynamicProxyFilterConfig.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();
}
}
- 동적 프록시를 빈으로 등록한 설정 파일(Filter 기능 적용)을 Import 한다.
- 실행해보면 no-log 가 사용하는 noLog() 메서드에는 로그가 남지 않는 것을 확인할 수 있다.
728x90
'스프링 핵심 원리(고급편)' 카테고리의 다른 글
| Ch06. 스프링이 지원하는 프록시 - 프록시 팩토리(소개) (0) | 2022.04.12 |
|---|---|
| Ch05. 동적 프록시 기술 - CGLIB (0) | 2022.04.11 |
| Ch05. 동적 프록시 기술 - JDK 동적 프록시 (0) | 2022.04.11 |
| Ch05. 동적 프록시 기술 - 리플렉션 (0) | 2022.04.11 |
| Ch04. 프록시 패턴과 데코레이터 패턴 - 인터페이스 기반 프록시와 클래스 기반 프록시 정리 (0) | 2022.04.10 |