개발을 할 때 기존 기능을 보존 하면서, 확장 해야 하는 경우가 있다. 원본을 어쩔 수 없이 수정 해야 할 때는 피할 수 없지만, 확장만 한다면, Proxy 를 사용 하는 경우 쉽게 확장이 가능 하다. 사용 방법을 알아 보자.
Proxy 는 기존 에 만들었던 Class 를 새로운 Class로 포장 했다고 생각 하면 쉽다.
public class Packing {
private Target target;
public Packing(Target target) {
this.target = target;
}
// doing...
}
위 소스를 주목 하자. 포장지 에 기존 Class 를 담았다. 하지만 먼가가 부족 하다. 함수를 연결해줄 고리가 필요 하다. 그것이 InvocationHandler 이다. java.lang.reflect 패키지에서 제공 하고 있다.
포장지에 연결 고리를 달아보자.
public class Packing implements InvocationHandler {
// doing...
@Override
public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable {
return paramMethod.invoke(paramObject, paramArrayOfObject);
}
}
invoke 라는 함수를 볼 수 있다. 포장지와, 기존 Class 를 연결 시켜주는 역할을 한다.
paramObject: targetClass (기존 Class) 를 넣는다.
paramMethod: targetClass 에서 실행 하고 싶은 메소드를 넣는다.
paramArrayOfObject: targetClass 에서 실행하고 싶은 메소드의 파라미터 값을 넣는다.
위 3가지 설정을 끝내면 준비 작업은 끝났다. 3가지 정보에 따라 포장지에서 기존 Class 함수를 실행 하게 된다. 샘플을 만들어 보자.
Github: https://github.com/whitelife/whitelife-sample/tree/master/src/main/java/kr/co/whitelife/sample/proxy
public class Target {
public void doProcess() {
System.out.println("doProcess.....");
}
public String doStringProcess(String string) {
System.out.println("doStringProcess.....");
return string;
}
}
public class Packing implements InvocationHandler {
private Target target;
public Packing(Target target) {
this.target = target;
}
public void doProcess() {
try {
System.out.println("Packing doProcess start.....");
this.invoke(this.target, this.target.getClass().getDeclaredMethod("doProcess", new Class[]{}), new Object[]{});
System.out.println("Packing doProcess end.....");
} catch (Throwable e) {
e.printStackTrace();
}
}
public void doStringProcess(String string) {
try {
System.out.println("Packing doStringProcess start.....");
String result = (String) this.invoke(this.target, this.target.getClass().getDeclaredMethod("doStringProcess", new Class[]{String.class}), new Object[]{string});
System.out.println(result);
System.out.println("Packing doStringProcess end.....");
} catch (Throwable e) {
e.printStackTrace();
}
}
@Override
public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable {
return paramMethod.invoke(paramObject, paramArrayOfObject);
}
}
public class ProxyTest {
public static void main(String[] args) {
Packing packing = new Packing(new Target());
packing.doProcess();
System.out.println();
packing.doStringProcess("doString");
}
}
샘플 작성이 끝났다면 실행 해 보자. 아래와 같은 출력을 볼 수 있다.
Packing doProcess start.....
doProcess.....
Packing doProcess end.....
Packing doStringProcess start.....
doStringProcess.....
doString
Packing doStringProcess end.....
여기 까지 왔다면, 성공 한 것이다. 기존에 각자 사용하던 Class를 Target 으로 생각하고, 포장지를 만들어서 사용하면 된다.
만약 Spring Framework 를 사용하는 경우, 직접 구현할 필요 없이 Spring AOP Around 기능을 사용하면 쉽게 처리가 가능 할 것이다. 위 샘플 코드와 비슷 하기 때문이다.