클래스의 프록시
자바의 다이내믹 프록시는 인터페이스 타입에만 적용 가능했다. 어떻게하면 클래스 타입의 프록시를 적용할 수 있을까?
인프런 백기선님의 강의 <더 자바, 코드를 조작하는 다양한 방법> 을 수강하면서 공부한 내용을 정리합니다.
public class UserClassService {
void join(User user) {
System.out.println("join = " + user);
}
void terminate(User user) {
System.out.println("terminate = " + user);
}
}
1. cglib 를 이용하는 방법
dependencies {
// https://mvnrepository.com/artifact/cglib/cglib
implementation 'cglib:cglib:3.3.0'
}
@Test
void cglib() {
MethodInterceptor handler = new MethodInterceptor() {
private final UserClassService userClassService = new UserClassService();
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
if (method.getName().equals("terminate")) {
System.out.println("proxy start");
Object invoke = method.invoke(userClassService, args);
System.out.println("proxy end");
return invoke;
}
return method.invoke(userClassService, args);
}
};
UserClassService userClassService = (UserClassService) Enhancer.create(UserClassService.class, handler);
userClassService.join(user);
userClassService.terminate(user);
//join = User@6f306067
//proxy start
//terminate = User@6f306067
//proxy end
}
2. byteBuddy library를 이용하는 방법
dependencies {
implementation 'net.bytebuddy:byte-buddy:1.14.0'
}
@Test
void byteBuddy() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Class<? extends UserClassService> proxyClass = new ByteBuddy()
.subclass(UserClassService.class)
.method(named("terminate"))
.intercept(InvocationHandlerAdapter.of(new InvocationHandler() {
UserClassService userClassService = new UserClassService();
@Override
public Object invoke(Object o, Method method, Object[] args) throws Throwable {
System.out.println("proxy start");
Object invoke = method.invoke(userClassService, args);
System.out.println("proxy end");
return invoke;
}
}))
.make()
.load(UserClassService.class.getClassLoader())
.getLoaded();
UserClassService userClassService = proxyClass.getConstructor(null).newInstance();
userClassService.join(user);
userClassService.terminate(user);
}
3. 한계
위의 두 방식 모두 대상 클래스의 서브 클래스를 프록시로 만들어 사용하는 방법이라서 대상 클래스가 상속을 막아놓은 경우 사용할 수 없다.
final 클래스인 경우
private 생성자만 존재하는 경우
interface 가 존재하면 interface 를 통해서 구현하는 것이 확장성과 유연성 면에서 좋다. 따라서 인터페이스가 존재하면 인터페이스의 프록시를 만들어 사용하자.
//1. final 로 상속을 막은 경우
//public final class UserClassService {
public class UserClassService {
//2. private 생성자로 상속을 막은 경우
//private UserClassService() { }
void join(User user) {
System.out.println("join = " + user);
}
void terminate(User user) {
System.out.println("terminate = " + user);
}
}
4. 참고
Last updated
Was this helpful?