cglib의 사용
다음과 같이 스프링, 하이버네이트 에도 Enhacer 클래스 내부에도 내장되어 있다.
pom.xml 추가
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
Enhancer의 활용
BookService bookService = (BookService) Enhancer.create(BookService.class, handler);
MethodInterceptor를 활용하여 handler 생성
MethodInterceptor handler = new MethodInterceptor() {
BookService bookService = new BookService();
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return method.invoke(bookService, args);
}
};
https://github.com/cglib/cglib/wiki
ByteBuddy의 활용
Proxy 클래스 생성 : ByteBuddy를 활용하여 BookService의 subClass를 만든다.
Class<? extends BookService> proxyClass = new ByteBuddy().subclass(BookService.class)
.make().load(BookService.class.getClassLoader()).getLoaded();
Proxy클래스의 인스턴스 생성
BookService bookService = proxyClass.getConstructor(null).newInstance();
특정 작업을 하고 싶을 때(with InvocationHandlerAdapter)
Class<? extends BookService> proxyClass = new ByteBuddy().subclass(BookService.class)
/*********************************************
* 여기에
* 특정 작업을 진행 한다.
*********************************************/
.make().load(BookService.class.getClassLoader()).getLoaded();
BookService bookService = proxyClass.getConstructor(null).newInstance();
- 가공하고 싶은 method를 intercept 하여 자바의 프록시패턴과 유사하게 InvocationHandler()를 활용한다.
- return으로 invoke(instance, 인자)를 리턴한다.
- return 하기 이전에 특정 작업을 실행하고 싶다면 invoke 전후로 작업을 수행하면 된다.
(아래 코드의 aaaaa , bbbbb 출력과 같이)
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("aaaaa");
Object invoke = method.invoke(bookService,args);
System.out.println("bbbbb");
return invoke;
}
}))
.make().load(BookService.class.getClassLoader()).getLoaded();
문제점
상속을 활용하는 방법이기 때문에 특정한 면에서는 제한이 따른다.
가령
1. 상수를 사용하는 경우
public final class BookService {
public void rent(Book book) {
System.out.println("rent : "+book.getTitle());
}
public void returnBook(Book book) {
System.out.println("returnBook : "+book.getTitle());
}
}
2. 생성자의 접근제어자가 private 한 경우
public class BookService {
private BookService() {
}
public void rent(Book book) {
System.out.println("rent : "+book.getTitle());
}
public void returnBook(Book book) {
System.out.println("returnBook : "+book.getTitle());
}
}
subClass를 만들 때 부모인 BookService의 디폴트 생성자의 접근제어자가 private으로 되어있다면
자식의 인스턴스가 생성될 때 부모의 인스턴스도 같이 올라가게 되는데 부모의 디폴트 생성자가 private이라면 부모의 인스턴스를 생성할 수 없기에 에러가 발생하는 것이다.
서브 클래스를 만드는 방법의 단점
-
상속을 사용하지 못하는 경우 프록시를 만들 수 없다.
-
Private 생성자만 있는 경우
-
Final 클래스인 경우
-
인터페이스가 있을 때는 인터페이스의 프록시를 만들어 사용할 것.
Code Link
https://github.com/mike6321/PURE_JAVA/tree/master/TheJava/proxy-pattern
'Java > Java' 카테고리의 다른 글
(JAVA) 쓰레드의 개요 (1) (0) | 2020.01.19 |
---|---|
(JAVA) 애노테이션 프로세서 (0) | 2019.12.28 |
(JAVA) 다이나믹 프록시 (0) | 2019.12.24 |
(JAVA) 프록시 패턴 (0) | 2019.12.24 |
(JAVA) 리플렉션 API(4) - 나만의 DI 프레임워크 만들어보기 (0) | 2019.12.24 |