Java JDK动态代理和CGLib动态代理都可以实现在运行时动态创建代理对象,从而在不修改源码的情况下增强或控制对目标对象的访问。JDK动态代理通过反射机制实现、它要求目标对象必须实现接口、而CGLib动态代理则通过继承目标类的方式实现,通常用于无法实现接口的类。接下来,我们将详细探讨如何实现JDK和CGLib动态代理。
一、JDK动态代理的实现
JDK动态代理是基于接口实现的,它使用java.lang.reflect.Proxy
类来创建代理对象,并且通过实现InvocationHandler
接口自定义方法在调用处理器中处理方法调用。
创建InvocationHandler实现类
首先,定义一个InvocationHandler
接口的实现类,此类会重写invoke
方法,以定义在方法调用时所执行的代理逻辑。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
// 目标对象,通过反射来调用目标方法
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用具体方法前可以添加自定义操作
System.out.println("Before method call");
// 通过反射调用目标对象的方法
Object result = method.invoke(target, args);
// 在调用具体方法后可以添加自定义操作
System.out.println("After method call");
return result;
}
}
使用Proxy创建代理对象
通过Proxy.newProxyInstance
方法创建一个实现了目标接口的代理对象。
import java.lang.reflect.Proxy;
public class ProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载器
target.getClass().getInterfaces(), // 目标类实现的接口
new MyInvocationHandler(target) // InvocationHandler实现类
);
}
}
二、CGLib动态代理的实现
CGLib通过继承目标类在运行时生成的子类来实现,它依赖于net.sf.cglib.proxy.Enhancer
类和MethodInterceptor
接口。
创建MethodInterceptor实现类
首先,定义一个MethodInterceptor
接口的实现类,类似InvocationHandler
,这个类也需要重写一个方法,不同的是这里重写的是intercept
方法。
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class MyMethodInterceptor implements MethodInterceptor {
// 实现MethodInterceptor接口的intercept方法,定义代理逻辑
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在调用方法前可以执行某些操作
System.out.println("Before method call");
// 调用代理对象父类的方法(即目标类的方法)
Object result = proxy.invokeSuper(obj, args);
// 在调用方法后可以执行某些操作
System.out.println("After method call");
return result;
}
}
使用Enhancer创建代理对象
使用Enhancer
类生成代理对象,Enhancer
是CGLib提供的一个类生成类。
import net.sf.cglib.proxy.Enhancer;
public class CglibProxyFactory {
public static Object getProxy(Class<?> clazz) {
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz); // 设置需要代理的类
enhancer.setCallback(new MyMethodInterceptor()); // 设置MethodInterceptor实现类
return enhancer.create(); // 创建代理对象
}
}
通过上面的步骤,我们可以生成任意类的子类并在运行时通过拦截器对方法调用进行控制。这两种动态代理的底层实现机制不同,但都达到了在不修改源码的前提下增强类的行为的目的。实际应用中,这两种代理有各自的使用场景,JDK动态代理由于只能针对实现了接口的类,因此使用面相对窄;而CGLib动态代理没有这个限制,但由于使用了继承,因此不能对final
类以及final
方法进行代理。
相关问答FAQs:
问题1: Java的JDK动态代理和CGLib动态代理分别是如何实现的?
答:Java的JDK动态代理是通过反射机制来实现的,它要求被代理的目标类必须实现一个接口,代理类在运行时动态生成,在调用代理方法时会通过反射调用目标类的方法。而CGLib动态代理则是通过继承目标类来实现的,代理类是目标类的子类,在调用代理方法时直接调用子类的方法,因此不要求目标类实现接口。
问题2: JDK动态代理和CGLib动态代理在使用场景上有什么区别?
答:JDK动态代理适用于基于接口的代理,它要求被代理的目标类实现一个接口,在运行时生成代理对象。而CGLib动态代理适用于对类进行代理,它直接继承目标类并生成代理对象。因此,如果目标类已经实现了接口,我们可以使用JDK动态代理;如果目标类没有实现接口或者我们想要代理类的所有方法,我们可以使用CGLib动态代理。
问题3: JDK动态代理和CGLib动态代理在性能上有什么区别?
答:JDK动态代理在生成代理对象时,需要通过反射调用目标类的方法,这个过程相对较慢,适用于对性能要求不高的场景。而CGLib动态代理通过继承目标类,直接调用子类的方法,省去了反射的过程,因此性能比JDK动态代理更高。但是CGLib生成的代理类需要动态加载到JVM中,所以在启动时会稍微慢一些。因此,在性能要求较高的场景下,可以考虑使用CGLib动态代理。