다이나믹 프록시
사전 지식
- java Class loading (실행과정)
- java 리플렉션
프록시 패턴
- 프록시와 리얼 서브젝트가 같은 인터페이스를 상속받고 클라이언트는 인터페이스를 프록시 타입으로 선언해서 사용한다.
- 프록시에서는 앞뒤로 부가 가능을 추가하고 리얼 서브젝트를 호출 한다.
- 결과적으로 리얼 서브젝트는 제공하는 핵심기능을 유지하며 코드 변경 없이 부가적인 기능(트렌젝션, 접근 제어, 로깅)을 제공할 수 있다.
다이나믹 프록시
- 테스트 코드 실행을 위해서 junit4를 사용할 것이다.
-
프록시 패턴 인터페이스가 필요하듯 인터페이스를 추가한다
public interface SomethingService { @MorningGreetings public void printName(String name); public void printinformation(String information); }
-
특정한 메소드에만 프록시 기능을 추가하고 싶기 떄문에 어노테이션을 추가하자
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MorningGreetings {}
-
real 서브젝트를 만들자
public class SomethingServiceImpl implements SomethingService { @Override public void printName(String name) { System.out.println(name); } @Override public void printinformation(String information) { System.out.println(information); } }
-
리얼 서브젝트를 감싸는 프록시 테스트할 코드를 작성하자
public class SomethingServiceProxyTest { public SomethingService somethingService = (SomethingService) Proxy.newProxyInstance(SomethingService.class.getClassLoader(), new Class[]{SomethingService.class}, new InvocationHandler() { SomethingService somethingService = new SomethingServiceImpl(); @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { MorningGreetings greetingAnocation = method.getAnnotation(MorningGreetings.class); if (greetingAnocation != null) { System.out.print("Hi,"); Object realInvoke = method.invoke(somethingService, objects); System.out.println("good morning"); return realInvoke; } return method.invoke(somethingService, objects); } }); @Test public void TestPrintName() { somethingService.printName("dobby"); somethingService.printinformation("dobby is cute"); } }
- java.lang.reflect.Proxy라는 API를 이용해서 프락시를 만들었다.
- 결과 값은…
Hi,dobby good morning dobby is cute
java 기본 API로는 class 기반의 프록시를 못만들어준다.
- 무조건 interface가 필요
- class로 하게 되면 “is not an interface"라는 에러메시지가 뜬다.
- class 기반 프록시를 하고 싶다면 추가 라이브러리가 필요하다.
cglib (추천)
- https://github.com/cglib/cglib/wiki
- spring 내부에서도 사용하는 라이브러리
- 단 하위 호환성이 좋지 않아서 각 프로젝트별로 가지고 있는 것이 좋음
ByteBuddy
- https://bytebuddy.net/#/
- 바이트 코드 조작 뿐 아니라 런타임(다이나믹) 프록시를 만들 때도 사용가능
- 서브클래스를 만드는 방법
- private 생성자만 있는 경우는 사용 못함
- final class인 경우는 사용 못함
다이나믹 프록시를 사용하는 곳
- 스프링 데이터 JPA
- 스프링 AOP
- Mockito
- 하이버네이트 lazy initialzation
- … 등등