둘셋 개발!

[JAVA] JDK 동적 프록시 원리와 장점 본문

카테고리 없음

[JAVA] JDK 동적 프록시 원리와 장점

23 2024. 11. 21. 11:09

 
intro.
회사에서 코드를 보던 중 AOP 기술을 사용한 곳을 보았다.
스프링의 장점 중 하나가 AOP(관점지향프로그래밍)이라는 것만 알고있었고 어떤 기술을 사용하는지는 몰라서 AOP에 대해 공부하려고 한다.
이번 포스팅은 AOP는 동적 프록시가 기반이기 때문에, 동적 프록시에 대해 알아보고자 한다.
 

구조

출처: 김영한 강사 - 스프링 핵심원리 고급 강의 자료

클라이언트가 메서드를 호출하면 proxy는 handler를 호출한다.
handler에는 부가기능로직과 실제 target (그림에서는 aImpl)을 호출하는 부분이 있다.
proxy는 동적으로 호출한 메서드 정보를 handler에게 넘겨준다.
handler는 메서들 정보를 넘겨받기 때문에 target의 메서드를 호출할 수 있다.
handler에선 부가기능로직 실행과 target의 메서드 호출을 한다.
 

Proxy 생성 

동적 Proxy를 생성할 때는 다음과 같이 생성한다.

AInterface proxy =
	(AInterface) Proxy.newProxyInstance(AInterface.class.getClassLoader(), new Class[]{AInterface.class}, handler);
  • 파라미터
    • target의 interface의 클래스 로더
    • target의 인터페이스
    • handler
  • 프록시가 어떤 target의 프록시인지 명시해야 하고 (target정보)
  • 어떤 프록시 기능을 사용해야하는지(handler)에 대한 정보가 필요하다.

handler

동적 프록시에 적용할 로직은 InvocationHandler 인터페이스를 구현해서 작성한다.

public class LogTraceBasicHandler implements InvocationHandler {

    private final Object target;
    private final String logTrace;

    public LogTraceBasicHandler(Object target, String logTrace) {
        this.target = target;
        this.logTrace = logTrace;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 프록시 역할 다하는 중
        String message = method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()";
        System.out.println("message = " + message + logTrace);

        // 로직 호출
        Object result = method.invoke(target, args);

        return result;
    }
}
  • 프록시의 메서드가 호출되면 핸들러가 낚아채서, invoke()를 수행
  • 핸들러에는 target과 호출된 method 정보가(리플렉션 사용) 있기 때문에
  • target의 실제 메서드를 호출할 수 있는 것이다.
💡 여기서 내가 헷갈렸던 내용은 Method 이다.
invoke()의 파라미터로 들어오는 method는 자바의 리플렉션이 사용되어,
동적으로 호출된 메서드의 정보가 들어있다.
따라서 메서드가 고정되어 있는 것이 아니라, 호출된 메서드가 이 파리미터로 들어오는 것이다.

 


정리

client가 메서드를 호출하면, proxy가 호출된다.
proxy를 처음 생성했을 때 파라미터로 전달한 handler가 호출된 메서드 정보 등을 넘겨받으면서 호출된다.
handler에서 로그찍기 등 프록시의 역할을 하고 넘겨받은 메서드를 가지고 target의 메서드를 호출한다.
원래는 target당 프록시를 각각 생성하여 그 안에 부가기능로직을 작성해야했는데,
동적 프록시를 사용하면 부가기능로직이라는 공통부분을 handler에 넣고
프록시들이 이를 공유해서 사용할 수 있다.
따라서 중복코드를 방지할 수 있게 된 것이다.