자바, 스프링이 프록시를 어떻게 만들까? - 2. 동적 프록시

Hyunta·2022년 11월 4일
0

프록시

목록 보기
2/4

동적 프록시

동적 프록시란 프록시 클래스를 일일이 만들지 않고 리플렉션을 이용해 해당 클래스가 가진 메서드를 실행시키는 방식으로 구현한다.

JDK 동적 프록시

만약 위에 프록시패턴을 적용한 것처럼 구현한다면 ProxyA, ProxyB를 각각 만들어줬어야 했을 것이다.

JDK 동적 프록시는 java에서 제공하는 동적 프록시 방식으로 인터페이스를 필수로 요구한다. 사용 방법은 간단하다, package java.lang.reflect; 에서 InvocationHandler 인터페이스 구현체를 만들어서 사용하면 된다.

InvocationHanlder 구현체에 호출할 Target 대상을 지정해준다. 위 다이어그램에서는 ImplA, ImplB를 전달해주는 것이다.

로그를 통해 실제 객체가 호출된 것이 아니라 proxyClass=class com.sun.proxy.$Proxy9 프록시 객체가 호출된 것을 알 수 있다. $Proxy#num 은 JDK 동적 프록시가 클래스 이름을 정하는 패턴이다.

프록시 객체의 생성은 JDK 동적 프록시가 해주고 우리는 해당 기능만 만들어주면 된다. 최종 구현형태는 아래 그림처럼 된다.

하지만, JDK 동적 프록시는 인터페이스가 있는 객체만 구현 가능하다. 스프링에서는 구체클래스만 있는 경우에도 동적 프록시를 제공하기 위해서 CGLIB 라이브러리를 활용한다.

CGLIB

Code Generator Library

  • 바이트 코드를 조작해서 동적으로 클래스를 생성하는 기술을 제공하는 라이브러리
  • 구체 클래스만 갖고 동적 프록시를 만들 수 있다.
  • 스프링 프레잌워크가 스프링 내부 소스 코드에 포함했다. 따라서 스프링 환경이라면 라이브러리 추가없이 사용할 수 있다.

사용방법은 JDK 동적 프록시와 유사하다. InvocationHandler를 만들었던 것처럼 MethodInterceptor를 만든다.

package org.springframework.cglib.proxy;
  public interface MethodInterceptor extends Callback {
      Object intercept(Object obj, Method method, Object[] args, MethodProxy
  proxy) throws Throwable;
  }


CGLIB은 Enhancer를 사용해서 프록시를 생성한다.

  • setSuperclass() : 구체 클래스를 상속받아서 프록시를 생성할 수 있다. 어떤 구체 클래스를 상속받을지 지정한다.
  • setCallback() : 프록시에 적용할 실행 로직을 할당한다. MethodInterceptor를 넣어주면 된다.

CGLIB이 프록시 객체에 이름을 주는 방식은 아래와 같다.

대상클래스$$EnhancerByCGLIB$$임의코드
hello.proxy.common.service.ConcreteService$$EnhancerByCGLIB$$25d6b0e3

런타임 의존 관계

클래스 의존 관계

추가로 CGLIB을 적용하기 위해선 객체에 기본 생성자가 있어야 한다.

동적 프록시 정리

동적 프록시를 사용하면 프록시 객체를 일일이 만들지 않기 때문이 기본 프록시 패턴을 사용했을 때의 문제점을 개선했다.

하지만, 이제 상황에 따라 인터페이스가 있을 경우 JDK 동적 프록시, 없을 경우 CGLIB을 쓴다던지 전략이 있어야 한다.

두 기술을 함께 사용하려면 InvocationHandler와 MethodInterceptor를 각각 중복으로 만들어서 관리해야할까?

특정조건에 맞을 때 프록시 로직을 적용하는 기능도 같이 제공해주길 바래서 나온 것이 스프링의 프록시 팩토리다.

Reference

김영한님 인프런 강의

profile
세상을 아름답게!

0개의 댓글