티스토리 뷰


자바 AOP JDK Dynamic Proxy를 경험해보기 위해서 테스트 클레스를 생성합니다. 지난번에 JDK Proxy에 대한 코멘트를 다음과 같이 했습니다.

Java Proxy같은 경우에 인터페이스를 반드시 구현해야하고 리플렉션을 사용하여 구현한 기술이기에 상대적으로 퍼포먼스가 떨어지는 특징을 갖습니다. 인터페이스를 극혐하는 저와 퍼포먼스가 떨어지는것도 싫어하는 저에게는 일단 탈락.

 

실제로 어떤지 구현을 통해 알아보면 뇌리에 쉽게 박히니 짬을 내서 JDK Dynamic Proxy 예제를 준비해보았습니다.

 

자바 AOP - JDK Dynamic Proxy 사용해보기 - Java AOP #2

실제로 어떤지 구현을 통해 알아보면 뇌리에 쉽게 박히니 짬을 내서 JDK Dynamic Proxy 예제를 준비해보았습니다.

1. JDK proxy는 인터페이스를 필수적으로 구현해야합니다.

 

//인터페이스가 필요합니다. 
interface Test{
    public void hello(String msg);
}

 

2. 당연히 인터페이스를 구현한 구현체도 만들어야겠죠?

 

//인터페이스를 구현한 구현체.
class TestImpl implements Test{

    @Override
    public void hello(String msg) {
        System.out.println(msg);
    }
    
}

 

3. java.reflection.InvocationHandler를 구현하는 핸들러를 구성합니다.

 

// 이렇게 InvocationHandler를 구현하는 핸들러를 구성합니다.
class DynamicInvocationHandler implements InvocationHandler {
    private Object target;
    private final Map<String, Method> methods = new HashMap();
    public DynamicInvocationHandler(Object target) {
        this.target = target;
        for (Method method : target.getClass().getDeclaredMethods()) {
            this.methods.put(method.getName(), method);
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke method name : " + method.getName() + ", args : " + args[0]);
        // 여기에 필요한 기능을 구현할수 있음.
        Object result = methods.get(method.getName()).invoke(target, args);
        return result;
    }
}

 

4. 이제 이 proxy를 돌려보는 코드를 만들어 실행해봅시다.

 

public class Exam01_JDKDynamicProxy {
    public static void main(String[] args) {

        // create proxy
        Test proxyInstance = (Test) Proxy.newProxyInstance(Exam01_JDKDynamicProxy.class.getClassLoader(),
                new Class[] { Test.class }, new DynamicInvocationHandler(new TestImpl()));

        System.out.println("created proxy");

        // proxy test
        proxyInstance.hello("hi 1");
        
        // java 8 lamda로 사용하는 방법
        Test test = new TestImpl();     
        Test proxyInstance2 = (Test) Proxy.newProxyInstance(
                Exam01_JDKDynamicProxy.class.getClassLoader(), 
                  new Class[] { Test.class }, 
                  (proxy, method, methodArgs) -> { 
                      System.out.println("invoke method 2 ");
                      return method.invoke(test, methodArgs);
                });
        proxyInstance2.hello("hi 2");
    }
}

 

출력 :

created proxy

invoke method name : hello, args : hi 1

hi 1

invoke method 2

hi 2

이렇게 리플렉션을 사용하니 느릴수밖에 없다는 사실을 알수 있었습니다. 더욱이 저의 경우는 spring framework에서도 인터페이스를 잘 안쓰는 데 일일이 인터페이스를 만들어서 구현해야한다니 개인적으로 무척 외면하고 싶어지는 기술입니다.

 

스프링 프레임워크에서는 기본적으로는 인터페이스가 있을 때엔 Proxy를 쓰게 되고 인터페이스가 없다면 CGLIB를 사용하지만 간단한 xml 설정만으로 jdk dynamic proxy와 CGLIB의 방식을 선택해서 사용할 수 있습니다.

스프링 프레임워크 config xml 파일 설정

 

<aop:config proxy-target-class="false"> 
//proxy-targetclass=true인 경우 CGLIB 사용, false인 경우 proxy
    ------   
    ------   
</aop:config>

 

다음시간엔 CGLIB에 대해서 상세히 공부해보고자 합니다.


댓글
최근에 올라온 글
최근에 달린 댓글