Home Java - JDK 동적 프록시
Post
Cancel

Java - JDK 동적 프록시

JDK 동적 프록시

동적 프록시 기술을 사용하면 개발자가 직접 프록시 클래스를 만들지 않고 동적으로 프록시 객체를 런타임에 생성 할 수 있다. 또한 동적 프록시에 원하는 실행 로직을 지정하는것도 가능하다.

JDK 동적 프록시 구현

JDK 동적 프록시는 인터페이스를 기반으로 프록시를 동적으로 만들기 떄문에 인터페이스가 필수 이다.

1
2
3
public interface AInterface {
  String call();
}
1
2
3
4
5
6
public class AImpl implements AInterface {@Override
  public String call() {
    log.info("A 호출");
    return "a";
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Slf4j
public class TimeInvocationHandler implements InvocationHandler {

  private final Object target;

  public TimeInvocationHandler(Object target) {
    this.target = target;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    log.info("TimeProxy 실행");
    long startTime = System.currentTimeMillis();

    Object result = method.invoke(target, args);

    long endTime = System.currentTimeMillis();
    long resultTime = endTime - startTime;
    log.info("TimeProxy 종료 resultTime={}", resultTime);
    return result;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
void dynamicA() {
  AInterface target = new AImpl();
  TimeInvocationHandler handler = new TimeInvocationHandler(target);

  AInterface proxy =
    (AInterface)Proxy.newProxyInstance(
      AInterface.class.getClassLoader(),
      new Class[]{AInterface.class},
      handler);

  proxy.call();
  log.info("targetClass={}", target.getClass());
  log.info("proxyClass={}", proxy.getClass());
}
TimeInvocationHandler - TimeProxy 실행
AImpl - A 호출
TimeInvocationHandler - TimeProxy 종료 resultTime=0
JdkDynamicProxyTest - targetClass=class hello.proxy.jdkdynamic.code.AImpl
JdkDynamicProxyTest - proxyClass=class com.sun.proxy.$Proxy1


로그를 보면 proxyClass=class com.sun.proxy.$Proxy1 이 부분이 동적으로 생성된 프록시 클래스 이다. 이는 개발자가 직접 만든 클래스가 아닌 JDK 동적 프록시가 이름 그래도 동적으로 만들어준 프록시 이다. 이 프록시는 TimeInvocationHandler 로직을 실행한다.

실행 순서 Alt text

  1. 클라이언트는 JDK 동적 프록시의 call() 을 실행
  2. JDK 동적 프록시는 InvocationHandler.invoke()를 호출
  3. TimeInvocationHandler가 구현체 이므로 TimeInvocationHandler.invoke()가 호출
  4. TimeInvocationHandler가 내부 로직을 수행하고, method.invoke(target, args)를 호출해서 target인 실제 객체(AImpl)를 호출
  5. AImpl 인스턴스의 call()이 실행
  6. AImpl 인스턴스의 call() 종료후 TimeInvocationHandler로 응답이 반환되고 그 응답을 외부로 반환

정리

JDK 동적 프록시 기술로 인하여 적용 대상 만클 프록시 객체를 만들지 않아도 된며, 같은 부가 기능로직을 한번만 개발해서 공통으로 적용할 수 있다. 결과적으로 프록시 클래스를 수 없이 만들어야 하는 문제도 해결하고 부가 기능 로직도 하나의 클래스에 모아서 단일 책임 원칙(SRP)도 지킬 수 있다.

JDK 동적 프록시 도입 전 - 직접 프록시 생성 Alt text

Alt text

JDK 동적 프록시 도입 후 Alt text

  • $Proxy1는 개발자가 직접 만드는 클래스가 아님

Alt text


참고

  • 스프링 핵심 원리 - 고급편(김영한)
This post is licensed under CC BY 4.0 by the author.