티스토리 뷰

Java

dynamic proxy 구현하기

LichKing 2018. 9. 1. 18:06

스프링에서 AOP를 구현하는 방법중 하나가 dynamic proxy 를 이용하는 방법이다. 특별한 내용은 아니고, dynamic proxy 를 직접 구현해보는 포스팅을 하나 하고자한다.


public interface Inter {
String toMessage();
}


먼저 인터페이스를 하나 정의해준다. 예제를 위한 인터페이스라 대충 만들었다. 이제 proxy 를 구현해주자. Proxy 클래스에있는 팩토리메서드를 호출하면 된다.


public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)

(호출해야할 메서드)


파라미터들을 확인해보자. 클래스로더와 구현해야할 인터페이스, 그리고 호출핸들러를 필요로하고있다. 하나하나 파고들면 너무 어려워지니... 일단은 구현해보자. 뭘 보내야할지 가장 난감한게 클래스로더일텐데, 현재 스레드의 컨텍스트 클래스로더를 보내주면 된다. 두번째는 인터페이스를 받긴하는데 잘 보면 배열이다. 배열로 보내주자.


Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{Inter.class})


이런형태로 넣어주면 된다. 이제 마지막 InvocationHandler 를 넣어줘야하는데 


public interface InvocationHandler


얘는 인터페이스다. 고로 구현해줘야한다.


new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
}


익명 클래스형태로 구현해준다. 3개의 파라미터를 받는데 proxy 자신과 method, args 를 받는다. method 객체의 invoke() 메서드를 호출하면 원래 호출하고자하는 메서드가 호출된다. args 는 해당 메서드의 인자이다.


new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(new Inter() {
@Override
public String toMessage() {
return "world";
}
}, args);
}
}


안에 구현된 Inter 익명클래스가 proxy 를 이용해 AOP 처리하고자하는 실제 객체이다. 얘를 위에 작성한 Proxy 에 전달하면 된다.


Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] {Inter.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(new Inter() {
@Override
public String toMessage() {
return "world";
}
}, args);
}
});

이제 proxy 객체를 담을 변수를 선언해주면 된다. 위에서 확인했다면 알겠지만 팩토리 메서드는 Object 타입을 반환하므로 형변환을 해줘야한다.


Inter inter = (Inter) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[] {Inter.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(new Inter() {
@Override
public String toMessage() {
return "world";
}
}, args);
}
});

그리고 호출해주자.


System.out.println(inter.toMessage());


world 라는 문자열이 찍히는걸 볼 수 있다. 이제 AOP 에 걸맞게 실제 호출 메서드 전에 내가 원하는 행위를 하도록 구현해보자.


Inter inter = (Inter) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[] {Inter.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("hello");

return method.invoke(new Inter() {
@Override
public String toMessage() {
return "world";
}
}, args);
}
});


world 앞에 hello 가 찍히게된다. 실무에서 직접 proxy 를 이용할 일은 거의 없다. 하지만 스프링을 공부하다보면 AOP 에 대한 공부가 필연적이고, Spring AOP 를 공부하다보면 JDK Dynamic Proxy 라는 말을 여러번 듣고, 보게된다. JDK Dynamic Proxy 가 무엇이고 대략적으로 어떻게 구현하는지 정도는 알고있으면 좋다. 그래야 왜 Spring AOP 에서 Dynamic Proxy 방식을 이용할때는 인터페이스 구현이 필수인지도 이해할수 있기 때문이다.

공유하기 링크
TAG
댓글
댓글쓰기 폼