반응형
Reflection
다음과 같은 로직을 살펴보자.
Hello target = new Hello();
//공통 로직1 시작
log.info("start");
String result1 = target.callA(); //호출하는 메서드가 다음
log.info("result={}", result1);
//공통 로직1 종료
//공통 로직2 시작
log.info("start");
String result2 = target.callB(); //호출하는 메서드가 다음
log.info("result={}", result2);
//공통 로직2 종료
공통로직을 이쁘게 묶는 방법이 필요하다... 이럴 때 사용하는게 reflection이다.
//클래스 정보
Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");
Hello target = new Hello();
//callA 메서드 정보
Method methodCallA = classHello.getMethod("callA");
Object result1 = methodCallA.invoke(target);
log.info("result1={}", result1);
//callB 메서드 정보
Method methodCallB = classHello.getMethod("callB");
Object result2 = methodCallB.invoke(target);
log.info("result2={}", result2);
getMethod 함수를 통해서 Method를 호출할수있다. 이때 invoke라는 메서드를 사용하게 되는데 해당 method를 콜하는 기능을 한다.
//클래스 정보
Class classHello = Class.forName("hello.proxy.jdkdynamic.ReflectionTest$Hello");
Hello target = new Hello();
Method methodCallA = classHello.getMethod("callA");
dynamicCall(methodCallA, target);
Method methodCallB = classHello.getMethod("callB");
dynamicCall(methodCallB, target);
private void dynamicCall(Method method, Object target) throws Exception {
log.info("start");
Object result = method.invoke(target);
log.info("result={}", result);
}
공통화 하기 어려울것 같았다. 공통로직을 dynamicCall이라는 메서드로 공통화 한다.
코드를 보면 알 수있지만 method.invoke를 통해서 함수를 호출하고 그 위 앞으로 감쌀수있는 전략을 구사할 수 있다.
한마디로.. 함수를 저장하고 나중에 호출?? 할 수 있는 방법이 생긴것..
이것을 생각하고 JDK 프록시를 보자.
먼저 InvocationHandler를 사용해야한다.
여기서는 위의 dynamicCall 같은 공통로직을 저장하는 곳이다. 이제 이 공통로직을 호출해보도록 하자.
@Slf4j
public class TimeInvocationHandler implements InvocationHandler {
private final Object target;
public TimeInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("TimeProxy 실행");
long startTime = System.currentTimeMillis();
Object result = method.invoke(target, args);
long endTime = System.currentTimeMillis();
long resultTime = endTime - startTime;
log.info("TimeProxy 종료 resultTime={}", resultTime);
return result;
}
}
다음과 같이 Proxy.newProxyInstance를 이용해서 프록시를 생성하고 필요한 인자값을 넣어준뒤 call을 하면
hadler에서 공통로직으로 감싼 부분은 공통로직으로 설정이 가능하다.
@Test
void dynamicA() {
AInterface target = new AImpl();
TimeInvocationHandler handler = new TimeInvocationHandler(target);
AInterface proxy = (AInterface) Proxy.newProxyInstance(AInterface.class.getClassLoader(), new Class[]{AInterface.class}, handler);
proxy.call();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
}
@Test
void dynamicB() {
BInterface target = new BImpl();
TimeInvocationHandler handler = new TimeInvocationHandler(target);
BInterface proxy = (BInterface) Proxy.newProxyInstance(BInterface.class.getClassLoader(), new Class[]{BInterface.class}, handler);
proxy.call();
log.info("targetClass={}", target.getClass());
log.info("proxyClass={}", proxy.getClass());
}
실제로는 필요한 interface에 공통로직이 필요하다면 @Bean으로도 등록할 수 있다.
@Bean
public OrderRepositoryV1 orderRepositoryV1(LogTrace logTrace) {
OrderRepositoryV1 orderRepository = new OrderRepositoryV1Impl();
OrderRepositoryV1 proxy = (OrderRepositoryV1) Proxy.newProxyInstance(OrderRepositoryV1.class.getClassLoader(),
new Class[]{OrderRepositoryV1.class},
new LogTraceBasicHandler(orderRepository, logTrace));
return proxy;
}
반응형
'개인공부' 카테고리의 다른 글
DDD 공부 : 마이크로서비스 (0) | 2023.06.28 |
---|---|
인터페이스 프록시 VS 클래스 기반 프록시 패턴 (0) | 2022.05.15 |
프록시 패턴 / 데코레이터 패턴 (0) | 2022.03.28 |
템플릿 콜백 패턴 (0) | 2022.03.28 |
전략 패턴 (0) | 2022.03.28 |