런타임 시점에 특정 인터페이스들을 구현하는 클래스, 인스턴스를 만드는 것
프록시 인스턴스 만들기
Object Proxy.newProxyInstance(ClassLoader, Interfaces, InvocationHandler)
BookService bookService = (BookService) Proxy.newProxyInstance(BookService.class.getClassLoader(), new Class[] {Bookservice.class}, //반드시 인터페이스 타입이어야 함. 클래스 타입은 안됨
new InvocationHandler(){
BookService bookService = new DefaultBookService();
@Overrid
public Object invoke(Object proxy, Method method, Obhect[] args) throws Throwable{
if(method.getName().equals("rent")){
System.out.println("aaa");
Object invoke = method.invoke(bookService, args);
System.out.println("bbb");
return invoke;
}
return method.invoke(bookService, args);
}
});
위 코드의 주석에서 언급한 바와 같이, 클래스 배열에서 클래스를 넘겨줄때 반드시 전달 클래스는 인터페이스 타입이어야만 한다고 했다. (자바에서 제공하는 다이나믹 프록시의 제약점)
이번에는 인터페이스가 없는 경우의 프록시 생성 방법에 대해 알아보고자 한다
서브 클래스를 만들 수 있는 라이브러리를 사용하여 프록시를 만들 수 있음
CGlib
스프링, 하이버네이트가 사용하는 라이브러리
버전 호환성이 좋지 않아 서로 다른 라이브러리 내부에 내장된 형태로 제공되기도 함
dependency 추가
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
프록시 생성
MethodInterceptor handler = new MethodInterceptor() {
BookService bookService = new BookService();
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return method.invoke(bookService, objects);
}
};
BookService bookService = (BookService) Enhancer.create(BookService.class, handler);
ByteBuddy
프록시 생성
Class<? extends BookService> proxyCLass = new ByteBuddy().subClass(BookService.class).method(named("rent")).intercept(InvocationHandlerAdapter.of(new InvocationHandler(){
BookService bookService = new BookService();
@Override
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable
{
System.out.println("aaa");
Object invoke = method.invoke(bookService, args);
System.out.println("bbb");
return invoke;
}
}))
.make().load(BookServie,class.getClassLoader()).getLoaded();
proxyClass.getConstructor(null).newInstance();
서브 클래스를 만드는 방법의 단점
다이나믹 프록시
다이나믹 프록시 사용처