개발을 할 때 기존 기능을 보존 하면서, 확장 해야 하는 경우가 있다. 원본을 어쩔 수 없이 수정 해야 할 때는 피할 수 없지만, 확장만 한다면, 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


  • Target
public class Target {

    public void doProcess() {
        System.out.println("doProcess.....");
    }

    public String doStringProcess(String string) {
        System.out.println("doStringProcess.....");
        return string;
    }

}

  • Packing
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);
    }
}

  • ProxyTest
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 기능을 사용하면 쉽게 처리가 가능 할 것이다. 위 샘플 코드와 비슷 하기 때문이다.


+ Recent posts