动态代理
在 Java
中,实现动态代理的方式主要有两种方式:JDK 动态代理和 CGLIB 动态代理,这两种代理方式在 Java 实现代理模式时最为常见。
代理模式的一般 UML 图如下:
客户端在调用 Subject
类型的类时,将会将这个请求转发到 Proxy
类中,在 Proxy
类中再调用 ReadlSubject
类中的具体实现,在 Proxy
类中就可以在调用这些方法时自定义一些额外的行为
代理模式的实现
首先,按照上文的 UML 类图关系,首先定义一个 Subject
接口:
// 自定义的 Subject 接口,接口定义行为
public interface MineSubject {
String getMessage();
}
定义一个具体的实现类 RealMineSubject
:
public class RealMineSubject implements MineSubject {
public String getMessage() {
return "This is RealMineSubject Message";
}
}
定义具体的代理类 MineSubjectProxy
:
public class MineSubjectProxy implements MineSubject {
private final MineSubject mineSubject; // 接口定义行为,因此只需要具有这种行为的具体实现类即可
public MineSubjectProxy(MineSubject mineSubject) {
this.mineSubject = mineSubject;
}
public String getMessage() {
before();
String message = mineSubject.getMessage();
System.out.println("Get Message: " + message);
after();
return message;
}
private void before() {
System.out.println("Before Method invoke.....");
}
private void after() {
System.out.println("After Method invoke.....");
}
}
现在在客户端(现在就是 main
方法所在的类),调用相关的接口方法:
public class MainApplication {
public static void main(String[] args) {
MineSubject realSubject = new RealMineSubject();
MineSubject mineSubject = new MineSubjectProxy(realSubject);
mineSubject.getMessage();
}
}
对应的输出结果如下:
以上,就是一个简单的静态代理的实现,静态代理的缺点很明显,每次都需要重新再去定义相关的 Proxy
类,在实际使用中将会产生大量的代理类,导致维护起来特别麻烦,为此,引入了动态代理的方式来简化这些任务。
JDK 动态代理
基本使用
依旧使用上文的静态代理的例子,来说明 JDK 动态代理的使用
现在将 Proxy
换成 JDK 的动态代理,首先需要定义一个实现 java.lang.reflect.InvocationHandler
接口的类,来定义一些在方法调用时要采取的行为:
public class JdkDynamicProxy implements InvocationHandler {
private final MineSubject mineSubject;
public JdkDynamicProxy(MineSubject mineSubject) {
this.mineSubject = mineSubject;
}
/**
* @param proxy: 表示当前代理执行 method 的代理对象,这个对象由 JDK 来管理
* @param method: 当前 proxy 代理的要执行的方法
* @param args: 执行的方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
// invoke 需要两个参数,一个是具体的实现类,一个是方法的参数
Object result = method.invoke(mineSubject, args);
System.out.println("Get Result: " + result.toString());
after();
return result;
}
private void before() {
System.out.println("Before Method invoke.....");
}
private void after() {
System.out.println("After Method invoke.....");
}
}
调用时通过 java.lang.reflect.Proxy
的静态工厂方法来创建对应的代理对象,这个代理对象与上文的 MineSubjectProxy
类是想对应的,具体的调用方式如下:
public static void main(String[] args) {
// 具体的实现类依旧是需要的
RealMineSubject realMineSubject = new RealMineSubject();
JdkDynamicProxy proxy = new JdkDynamicProxy(realMineSubject);
// 获取实现类的类加载器,这是为了在实例化 Proxy 对象时使用的类加载器
ClassLoader loader = realMineSubject.getClass().getClassLoader();
/**
* 生成代理类对象
*/
MineSubject subject = (MineSubject) Proxy.newProxyInstance(loader, new Class[]{MineSubject.class}, proxy);
// 可以直接使用了。。。
subject.getMessage();
}
是不是看起来似乎 JDK 的动态代理并没有减少工作量,反而需要定义一些额外的实现类 ?实际上,通过上文的 JdkDynamicProxy
类,能够对传入的不同接口和不同的具体实现类,能够更加富有弹性地对不同的实现类进行代理,因此相比较上文提到的静态代理,还是要减少了工作量
实现原理
主要的实现在生成 Proxy
实例对象中,具体的源代码如下:
public static Object newProxyInstance(
ClassLoader loader, // 类加载器
Class<?>[] interfaces, // 要代理的接口列表
InvocationHandler h // 实际的处理对象,通过调用 invoke 方法来实现
) {
// 省略一部分代码
// 通过传入的接口得到一个代理类的构造函数对象
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
// 通过得到的构造函数和传入的 InvocationHandler 对象,生成一个新的 Proxy 实例
return newProxyInstance(caller, cons, h);
}
获取代理类的构造函数
-
获取代理类的构造函数之前的准备
具体地,获取代理接口的构造函数的源代码如下:
private static Constructor<?> getProxyConstructor( Class<?> caller, ClassLoader loader, Class<?>... interfaces ){ // optimization for single interface if (interfaces.length == 1) { Class<?> intf = interfaces[0]; // 核心代码,通过构建者模式的方式来创建对应的 Constructor 对象 return proxyCache.sub(intf).computeIfAbsent( loader, (ld, clv) -> new ProxyBuilder(ld, clv.key()).build() ); } // 省略一部分当传入的 interface 列表长度大于 1 时的额外处理代码,处理逻辑与单个的 interface 类似 }
创建
ProxyBuilder
的源代码如下:// 当传入的 interface 的数量为 1 时,将会将其包装成一个列表在进入此方法 ProxyBuilder(ClassLoader loader, List<Class<?>> interfaces) { //省略一部分条件检查的代码 // 这个方法的核心部分,主要的任务是获取 interfaces 的所有非静态引用类型:包括返回类型、参数类型、异常类型 Set<Class<?>> refTypes = referencedTypes(loader, interfaces); this.interfaces = interfaces; this.module = mapToModule(loader, interfaces, refTypes); assert getLoader(module) == loader; }
获取引用类型的源代码:
private static Set<Class<?>> referencedTypes( ClassLoader loader, List<Class<?>> interfaces ) { var types = new HashSet<Class<?>>(); // 遍历每个 Interface,获取使用到的类型 for (var intf : interfaces) { for (Method m : intf.getMethods()) { // 注意,这里的引用类型不包括基本数据类型,如 int、long 等 if (!Modifier.isStatic(m.getModifiers())) { addElementType(types, m.getReturnType()); // 返回类型 addElementTypes(types, m.getSharedParameterTypes()); // 方法参数类型 addElementTypes(types, m.getSharedExceptionTypes()); // 抛出的异常类型 } } } return types; }
-
正式构建
Proxy
的构造函数对象ProxyBuilder
对象的build()
方法如下:Constructor<?> build() { // 定义 Proxy 类,重点代码。。。。 Class<?> proxyClass = defineProxyClass(module, interfaces); final Constructor<?> cons; try { // constructorParams = InvocationHandler.class cons = proxyClass.getConstructor(constructorParams); } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); return cons; }
defineProxyClass(moudle, interfaces)
对应的源代码:private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) { String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /** * 记录所有的非 public 代理接口的包,这些接口的代理类将会被定义在这些包下面 */ for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; // non-public, final String pkg = intf.getPackageName(); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } // 完善要创建的类的包名 if (proxyPkg == null) { // 默认的包名:com.sun.proxy'.模块名'(如果存在) proxyPkg = m.isNamed() ? PROXY_PACKAGE_PREFIX + "." + m.getName() : PROXY_PACKAGE_PREFIX; } else if (proxyPkg.isEmpty() && m.isNamed()) { throw new IllegalArgumentException( "Unnamed package cannot be added to " + m); } if (m.isNamed()) { if (!m.getDescriptor().packages().contains(proxyPkg)) { throw new InternalError(proxyPkg + " not exist in " + m.getName()); } } /* * 为新定义的 Proxy 类定义一个全限定名(上文已经完善了包名,因此这里定义的类名将会是全限定名) * 生成的简单类名为 Proxy + 序列号,这个序列号是通过全局的原子整型增加来得到的 */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg.isEmpty() ? proxyClassNamePrefix + num : proxyPkg + "." + proxyClassNamePrefix + num; ClassLoader loader = getLoader(m); // 获取当前模块下的类加载器 trace(proxyName, m, loader, interfaces); /** * 这里会产生对应的 Proxy 类的字节码,涉及到 .class 文件的一系列内容,知道这个过程即可 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags); // 将得到的 Proxy 类的字节码加载到 JVM 中,得到这个 Proxy 类的类对象 Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length, loader, null); reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE); // 省略一部分一场捕获代码 return pc; }
至此,已经得到了生成的 Proxy 的 Class 对象,再回到
ProxyBuilder
的build()
方法:Constructor<?> build() { // 定义 Proxy 类,重点代码。。。。 Class<?> proxyClass = defineProxyClass(module, interfaces); final Constructor<?> cons; try { // 通过反射的方式,加入对应的参数类型来找到对应的构造函数 cons = proxyClass.getConstructor(constructorParams); } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } // 修改定义的 Proxy 的访问权限,将它定义为是可以访问的,这样在别的地方就能够实例化一个新的 Proxy 对象 AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); return cons; }
获取 Proxy 对象的构造函数完成
通过构造函数获取实例对象
这一步比较简单,由于之前已经通过反射设置了 Proxy 类的构造函数为可访问的,因此,很容易通过反射的方式对构造函数进行调用以得到对应的实例对象
private static Object newProxyInstance(
Class<?> caller, // null if no SecurityManager
Constructor<?> cons,
InvocationHandler h
) {
// 省略一部分许可检查和异常捕获的代码
return cons.newInstance(new Object[]{h}); // 创建实例对象的代码
}
CGLIB 动态代理
使用 JDK 动态代理的一个特点是代理时必须存在相对应的要代理的接口(即必须存在实现类的父接口),如果想取消这个限制,可以考虑使用 CGLIB 的动态代理
基本使用
由于 CGLIB 是一个第三方库,因此使用时需要首先引入这个库:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
现在,定义一个实体类 RealMine
:
public class RealMine {
public String sayMessage() {
return "This is RealMine Message";
}
}
通过 CGLIB 可以直接对这个类采取进行相关的代理方法,再次之前,需要定义一个实现了 net.sf.cglib.proxy.MethodInterceptor
接口的具体类,用于定义一些相关的行为
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
/**
* @param o : 对应 UML 图中的 RealSubject 对象
* @param method : 代理的方法
* @param objects : 传入代理代理方法的参数
* @param methodProxy : 实际代理对象
*/
public Object intercept(
Object o,
Method method,
Object[] objects,
MethodProxy methodProxy
) throws Throwable {
before();
Object message = methodProxy.invokeSuper(o, objects);
System.out.println("Get Message: " + message.toString());
after();
return null;
}
private void before() {
System.out.println("Before Method invoke.....");
}
private void after() {
System.out.println("After Method invoke.....");
}
}
客户端调用:
Enhancer enhancer = new Enhancer(); // 在代理时会生成一个子类
enhancer.setSuperclass(RealMine.class); // 设置这个代理对象的父类
enhancer.setCallback(new CglibProxy()); // 代理对象会采取的行为
RealMine realMine = (RealMine) enhancer.create(); // 创建实际代理对象
realMine.sayMessage(); // 调用这个代理对象的方法
得到的输出如下:
因为 CGLIB 是通过反射的方式直接获取相关的属性,而这种方式自 JDK 9 开始就已经被禁止使用了,因此会看到以上的警告
实现原理
CGLIB 是通过 ASM 字节码处理框架在运行期间扩展 Java 类与实现接口,相比较与 JDK 的动态代理,CGLIB 不需要一个公共的父接口即可对现有的类进行扩展来实现代理,更加简单方便。但是它也有以下的一些缺点:
- 由于是通过反射的方式来直接获取属性的,而这种方式在 JDK 9 开始就已经被禁止了,因此会看到上文中出现的警告信息
- CGLIB 对于类的扩展也是有限制的,对于
final
修饰的类和方法是无法完成代理的(因为final
修饰的类和方法无法被继承) - CGLIB 属于第三方的依赖包,因此在使用时对于环境就有一定的限制
具体的实现原理与 JDK 的动态代理类似,因此在此不在这里详细介绍
两者的比较
- JDK 动态代理利用拦截器(拦截器必须实现
InvocationHanlder
)加上反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokerHandler
来处理。只能对实现了接口的类生成代理 - CGLIB 利用 ASM 字节码处理框架,将代理对象类的 Class 文件加载进来,通过修改字节码生成子类的方式来进行处理。主要是对指定的类生成一个子类,覆盖其中的方法并且实现增强,但是由于采用的方法是继承的方式,因此对于 final 修饰的类和方法,无法被继承并增强
- 对于运行效率,这两者之间没有太大的区别,但是由于 JDK 的动态代理在执行时首先会调用 Proxy 类的
invoke
方法,由于这一次的调用可能会降低运行效率,但是这点消耗是完全可以忽略的。