动态代理是一种创建代理对象的方式,它允许在运行时创建代理类和对象,而不是在编译时创建。这种方式通常用于 AOP 编程中,可以在不改变目标类的情况下给它添加额外的功能。
具体来说,它可以用来在目标类的方法调用前后执行额外的逻辑,例如权限检查、性能监控、事务处理等。另外,它还可以用来实现远程代理、缓存代理、智能引用等功能。
目标类需要实现一个接口,通过查看生成的 Class 文件内容就可以知道 JDK 动态代理的机制,是依赖于目标类的接口而实现的
// TargetInterface 接口是为了创建 JDK 代理
public class Target implements TargetInterface {
public void method() {
System.out.println("Target method");
}
}
增强类需要实现 InvocationHandler
接口及方法,就可以在 invoke
方法中进行增强。在这里可以看到调用目标方法是通过反射的方式。
public class JdkProxy implements InvocationHandler {
private final Object target;
public JdkProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("JdkProxy invoke");
return method.invoke(target, args);
}
}
JDK 动态代理只需要直接调用 Proxy.newProxyInstance(...)
就可以创建动态代理。
// 输出动态代理类 Class 文件到 com/sun/proxy 但在生产环境中不建议使用。
// JDK1.8 前配置
//System.setProperties("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// JDK1.8 后配置
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
// 创建 Proxy
TargetInterface target = (TargetInterface) Proxy.newProxyInstance(
Target.class.getClassLoader(),
Target.class.getInterfaces(),
new JdkProxy(new Target())
);
// 执行目标方法
target.method();
生成的 Class 文件是继承 Proxy
并实现了目标类实现的接口 TargetInterface
并
public Target$Proxy extends Proxy implements TargetInterface {
public final void method() throws {
try {
// h 是自定义实现了 InvocationHandler 的代理类
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}
JDK 动态代理是通过 Java 反射机制来实现的。它可以在运行时动态创建一个实现了一组给定接口的代理类的实例。
JDK 动态代理的优点是使用简单,只需要提供一个实现了 InvocationHandler 接口的类即可。缺点是只能代理接口,不能代理类,而且被代理类必须实现接口
InvocationHandler
接口的类,它负责处理代理类上的方法调用Proxy.newProxyInstance()
方法创建代理类实例InvocationHandler
接口的类即可。通过 JDK 动态代理实现 AOP 不需要额外的框架和库,是很简单的。但是如果需要高效的性能,或者需要支持类代理,或者需要织入源码的方式来实现 AOP,那么需要使用其它的 AOP 框架例如接下来说的 CGLIB。
目标类区别于 JDK 动态代理,CGLIB 不需要实现任何接口,因为 CGLIB 的动态代理是通过继承目标类实现的
public class Target {
public void method() {
System.out.println("Target method");
}
}
增强类需要实现 MethodInterceptor
接口及方法,就可以在 intercept
方法中进行增强。
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CglibProxy invoke");
// 调用 invokeSuper 调用目标类
return methodProxy.invokeSuper(o, objects);
}
}
创建动态代理需要创建 Enhancer
对象,然后通过增强器配置目标类 Class、增强对象,由增强器来创建动态代理。
public static void main(String[] args) {
// 配置输出 CGLIB 动态代理生成的 Class 文件
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Target.class);
// 设置增强类
enhancer.setCallback(new CglibProxy());
// 创建动态代理
Target target = (Target) enhancer.create();
System.out.println("Create JDK Proxy");
target.method();
}
生成的 Class 文件包含多个,这里我们主要看继承目标类并实现接口 Factory
的增强 Class
public class Target$$EnhancerByCGLIB$$f6156be8 extends Target implements Factory {
public final void method() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$method$0$Method, CGLIB$emptyArgs, CGLIB$method$0$Proxy);
} else {
// 从这里的调用可以知道,CGLIB 增强调用是通过子类调用父类的实现的形式
super.method();
}
}
}
CGLIB 动态代理是通过继承来实现的。它可以在运行时动态创建一个继承自目标类的子类来实现代理。
CGLIB动态代理的优点是可以代理任意类,不需要实现接口。缺点是生成的代理类不能被继承。
CGLIB 主要应用于 Spring 框架中,基于 ASM 的字节码操作库,可以直接对类进行代理,而不需要接口。 CGLIB 动态代理相对于 JDK 动态代理,性能更高,但是也有其局限性,例如不能对 final 类进行代理。
JDK 和 CGLIB 动态代理是两种不同的代理方式,主要差异如下:
总的来说, JDK 动态代理适用于接口代理,它生成的代理类继承了 Proxy 类,而 CGLIB 动态代理适用于类代理,它生成的代理类继承了被代理类。