AImpl과 BImpl의 정의 두 객체는 단지 호출되었음을 확인 하기 위한 객체이며 별다른 역할을 가지고 있지는 않다.
interface AInterface {
String call();
}
class AImpl implements AInterface {
@Override
public String call() {
System.out.println("A 호출");
return "a";
}
}
interface BInterface {
String call();
}
class BImpl implements BInterface {
@Override
public String call() {
System.out.println("B 호출");
return "b";
}
}
리플렉션을 이용한 동적프록시 클래스 생성 및 메서드의 실행시간을 측정하는 로직 구현
class MyProxyHandler implements InvocationHandler {
private final Object target;
MyProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("TimeProxy 실행");
long startTime = System.nanoTime();
Object result = method.invoke(target, args); // 파라미터로 전달받은 메서드를 invoke로 실행
long endTime = System.nanoTime();
long resultTime = endTime - startTime;
System.out.println("TimeProxy 종료 resultTime = " + resultTime);
return result;
}
}
아래 코드와 같이 여러 개의 프록시 객체를 구현하지 않더라도 newProxyInstance를 통해 실행시킬 객체를 넣고 동적으로 프록시 객체를 반환받아 사용할 수 있다.
public class Client {
public static void main(String[] args) {
AInterface proxyA = (AInterface) Proxy.newProxyInstance(
AInterface.class.getClassLoader(),
new Class[]{AInterface.class},
new MyProxyHandler(new AImpl())
);
proxyA.call();
BInterface proxyB = (BInterface) Proxy.newProxyInstance(
BInterface.class.getClassLoader(),
new Class[]{BInterface.class},
new MyProxyHandler(new BImpl())
);
proxyB.call();
}
}
위의 방법 외에도 CGLIB을 이용한 방법도 가능하다. CGLIB은 인터페이스가 아닌 클래스를 활용하며, 바이트 코드를 조작해서 프록시를 생성하므로 기본적인 성능이 좋다.
물론 단점도 존재한다. 상속과 관련하여 오버라이딩을 지원하지 않는 경우 사용이 불가능 하다.