Spring 핵심원리 고급편 - Dynamic Proxy 2

목차

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);

// 로직 호출
method.invoke(target, args);
logTrace.end(status);

return result;
} catch (Exception ex) {
logTrace.exception(status, ex);
throw ex;
}
}
}

@Configuration
public class DynamicBasicProxyConfig {

@Bean
public OrderControllerV1 orderControllerV1(LogTrace logTrace){
OrderControllerV1 orderControllerV1 = new OrderControllerV1Impl(orderServiceV1(logTrace));

// Proxy 객체 생성
OrderControllerV1 proxy = (OrderControllerV1) Proxy
.newProxyInstance(
OrderControllerV1.class.getClassLoader(),
new Class[]{OrderControllerV1.class},
new LogTraceBasicHandler(orderControllerV1, logTrace));

return proxy;
}

@Bean
public OrderServiceV1 orderServiceV1(LogTrace logTrace){
OrderServiceV1 orderServiceV1 = new OrderServiceV1Impl(orderRepositoryV1(logTrace));

// Proxy 객체 생성
OrderServiceV1 proxy = (OrderServiceV1) Proxy
.newProxyInstance(
OrderServiceV1.class.getClassLoader(),
new Class[]{OrderServiceV1.class},
new LogTraceBasicHandler(orderServiceV1, logTrace));

return proxy;
}

@Bean
public OrderRepositoryV1 orderRepositoryV1(LogTrace logTrace){
OrderRepositoryV1 orderRepository = new OrderRepositoryV1Impl();

// Proxy 객체 생성
OrderRepositoryV1 proxy = (OrderRepositoryV1) Proxy
.newProxyInstance(
OrderRepositoryV1.class.getClassLoader(),
new Class[]{OrderRepositoryV1.class},
new LogTraceBasicHandler(orderRepository, logTrace));

return proxy;
}
}

@Import(DynamicBasicProxyConfig.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();
}
}
2021-11-28 22:18:58.331  INFO 3357 --- [nio-8080-exec-1] h.p.trace.logtrace.ThreadLocalLogTrace   : [4e72efcd] OrderControllerV1.request()
2021-11-28 22:18:58.333 INFO 3357 --- [nio-8080-exec-1] h.p.trace.logtrace.ThreadLocalLogTrace : [4e72efcd] |-->OrderServiceV1.orderItem()
2021-11-28 22:18:58.333 INFO 3357 --- [nio-8080-exec-1] h.p.trace.logtrace.ThreadLocalLogTrace : [4e72efcd] | |-->OrderRepositoryV1.save()
2021-11-28 22:18:59.338 INFO 3357 --- [nio-8080-exec-1] h.p.trace.logtrace.ThreadLocalLogTrace : [4e72efcd] | |<--OrderRepositoryV1.save() time=1005ms
2021-11-28 22:18:59.338 INFO 3357 --- [nio-8080-exec-1] h.p.trace.logtrace.ThreadLocalLogTrace : [4e72efcd] |<--OrderServiceV1.orderItem() time=1006ms
2021-11-28 22:18:59.339 INFO 3357 --- [nio-8080-exec-1] h.p.trace.logtrace.ThreadLocalLogTrace : [4e72efcd] OrderControllerV1.request() time=1007ms

특정 메소드에만 부가기능 로직 적용

PatternMatchUtils.simpleMatch 를 사용해 메소드 이름이 조건을 만족하는지 Pattern Matching 한다.

  • pattern : pattern 과 정확히 매칭되면 참
  • pattern* : pattern 으로 시작하면 참
  • *pattern : pattern 으로 끝나면 참
  • *pattern* : pattern 이 있으면 참
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);

// 로직 호출
Object result = method.invoke(target, args);
logTrace.end(status);

return result;
} catch (Exception ex) {
logTrace.exception(status, ex);
throw ex;
}
}
}
public class DynamicProxyFilterConfig {

private static final String[] PATTERNS = {"request*", "order*", "save*"};

@Bean
public OrderControllerV1 orderControllerV1(LogTrace logTrace){
OrderControllerV1 orderControllerV1 = new OrderControllerV1Impl(orderServiceV1(logTrace));
OrderControllerV1 proxy = (OrderControllerV1) Proxy.newProxyInstance(OrderControllerV1.class.getClassLoader(),
new Class[]{OrderControllerV1.class},
new LogTraceFilterHandler(orderControllerV1, logTrace, PATTERNS));

return proxy;
}

@Bean
public OrderServiceV1 orderServiceV1(LogTrace logTrace){
OrderServiceV1 orderServiceV1 = new OrderServiceV1Impl(orderRepositoryV1(logTrace));
OrderServiceV1 proxy = (OrderServiceV1) Proxy.newProxyInstance(OrderServiceV1.class.getClassLoader(),
new Class[]{OrderServiceV1.class},
new LogTraceFilterHandler(orderServiceV1, logTrace, PATTERNS));

return proxy;
}

@Bean
public OrderRepositoryV1 orderRepositoryV1(LogTrace logTrace){
OrderRepositoryV1 orderRepository = new OrderRepositoryV1Impl();

OrderRepositoryV1 proxy = (OrderRepositoryV1) Proxy.newProxyInstance(OrderRepositoryV1.class.getClassLoader(),
new Class[]{OrderRepositoryV1.class},
new LogTraceFilterHandler(orderRepository, logTrace, PATTERNS));

return proxy;
}
}
@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();
}
}
Share