动态代理是怎么 “动” 起来的?
代理模式可以说是应用最为广泛的设计模式之一,同时也是其他一些设计模式的基础或组成部分。
在上篇文章 深入浅出 Retrofit 中,就是通过 动态代理 来实现具体的网络请求逻辑。本着刨根究底的原则,这篇文章来探究一下动态代理的技术原理。
静态代理
在这之前,先来看一下基础的 静态代理 。
接口类 ICount
和实现类 Counter
:
public interface ICount {
void test1();
void test2();
}
public class Counter implements ICount {
@Override
public void test1() {
System.out.println("test1");
}
@Override
public void test2() {
System.out.println("test2");
}
}
现在要加一个需求,统计 Counter
类中每个方法的耗时。本着开闭原则,对扩展开放,对修改关闭,最好不要去动 Counter 原本的逻辑,可以提供一个同样实现 ICount
的代理类 CounterProxy
:
public class CounterProxy implements ICount {
private Counter counter;
public CounterProxy(Counter counter) {
this.counter = counter;
}
@Override
public void test1() {
long start = System.currentTimeMillis();
counter.test1();
long end = System.currentTimeMillis();
System.out.println("test1" + " consume: " + (end - start));
}
@Override
public void test2() {
long start = System.currentTimeMillis();
counter.test2();
long end = System.currentTimeMillis();
System.out.println("test2" + " consume: " + (end - start));
}
}
这就是 静态代理 的基本使用。
看起来很简单,但是应用场景也很明显。在需要对接口方法做统一处理的场景下,典型的如 Retrofit,静态代理就是个灾难,你得为每一个接口方法都提供实现。
让静态代理动起来
有没有办法把这个过程自动化呢?这就是 动态代理 的功能:自动生成代理类。
自动生成 Class 文件的方案很多,生写硬怼,Javassist,ASM 都可以。那么,是不是照着静态代理类的结构,在运行时自动生成就可以了呢?
这样设计显然是不合理的,为每个接口方法动态生成实现逻辑,那每个方法单独的处理逻辑从哪来呢?
合理的做法应该把所有接口方法桥接收拢,统一代理给一个接口方法 invoke()
,将 方法名称和参数 作为 invoke()
方法的参数,这样动态代理的使用者就可以针对 methodName 进行对应处理。
现在你可以猜想一下动态生成的代理类的具体结构:
-
代理类不依赖具体的实现类,所以它也应该实现被代理的接口
-
代理类需要把所有接口方法的实现桥接给同一个接口,假设叫做
InvocationHandler.invoke()
用伪代码表示:
public class DynamicProxy implements ICount { private Method method1 = Class.forName("proxy.ICount").getMethod("test1"); private Method method2 = Class.forName("proxy.ICount").getMethod("test2"); private InvocationHandler handler; public DynamicProxy(InvocationHandler handler) { this.handler = handler; } @Override public void test1() { handler.invoke(this, method1, (Object[])null) } @Override public void test2() { handler.invoke(this, method2, (Object[])null) } }
除了一些代码细节,这其实已经很接近真正生成的动态代理类了。
我们再来看 JDK 中动态代理的使用方法:
Counter counter = new Counter(); ICount proxyCount = (ICount) Proxy.newProxyInstance(counter.getClass().getClassLoader(), counter.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long start = System.currentTimeMillis(); Object result = method.invoke(counter, args); long end = System.currentTimeMillis(); System.out.println(method.getName() + " consume: " + (end - start)); return result; } }); proxyCount.test1(); proxyCount.test2();
核心点在于通过
Proxy.newProxyInstance()
动态生成代理类。它有三个参数:ClassLoader loader
: 类加载器Class<?>[] interfaces
:要代理的接口InvocationHandler h
: 所有接口方法会被桥接到 InvocationHandler 的invoke()
方法。
invoke()
方法有三个参数。proxy
是代理类,method
是代理的方法,args
是代理方法的参数。既满足了对代理方法的统一处理,也可以针对 method 做单独处理。完全符合我们之前的伪代码。
通过下面的 JDK 的参数配置,可以在当前目录直接生成动态代理类的 class 文件。
System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
你可以在 这里 查看生成的
com.sun.proxy.$Proxy0.class
文件。源码解析
动态代理的原理很简单,运行时动态生成并加载代理类 。我们跟进源码,再加深一下印象。
PS:以下代码基于 OpenJdk 15 。
从
Proxy.newProxyInstance()
开始。public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) { Objects.requireNonNull(h); final Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass(); // 1. 获取代理类的 Constructor Constructor<?> cons = getProxyConstructor(caller, loader, interfaces); // 2. 创建代理类实例 return newProxyInstance(caller, cons, h); }
先看代码 2 处的
newProxyInstance()
:private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager Constructor<?> cons, InvocationHandler h) { try { if (caller != null) { // 1. 权限检查 checkNewProxyPermission(caller, cons.getDeclaringClass()); } // 2. 创建代理类实例 return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException | InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { ...... } }
直接调用
Constructor.newInstance()
方法创建代理类实例,注意构造器参数是传入的InvocationHandler
对象。重点在于如何获取代理类的构造器?再回到代码 1 处的
getProxyConstructor()
。private static Constructor<?> getProxyConstructor(Class<?> caller, ClassLoader loader, Class<?>... interfaces) { // 单接口的优化处理 // 为了方便,我们直接看这里 if (interfaces.length == 1) { Class<?> intf = interfaces[0]; if (caller != null) { checkProxyAccess(caller, loader, intf); } // 从 proxyCache 中获取,或者通过 ProxyBuilder 新建 return proxyCache.sub(intf).computeIfAbsent( loader, (ld, clv) -> new ProxyBuilder(ld, clv.key()).build() ); } else { ...... } }
这里提供了 Constructor 的缓存类
proxyCache
避免重复生成。如果缓存中没有,就通过ProxyBuilder
获取构造器,传入的参数是 Classloader 和 Interface 。直接看
ProxyBuilder.build()
方法。Constructor<?> build() { // 1. 获取代理类的 Class 对象 Class<?> proxyClass = defineProxyClass(module, interfaces); final Constructor<?> cons; try { // 2. 根据代理类的 Class 对象获取构造器 cons = proxyClass.getConstructor(constructorParams); } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } ...... return cons; }
核心在于获取代理类 Class 对象的
defineProxyClass()
方法。private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) { String proxyPkg = null; // 代理类是 public final 的 int accessFlags = Modifier.PUBLIC | Modifier.FINAL; ...... if (proxyPkg == null) { // PROXY_PACKAGE_PREFIX 值为 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); } ...... /* * proxyClassNamePrefix 值为 $Proxy * 生成的代理类名 com.sun.proxy.$Proxy0 * 结尾数字从 0 开始递增 */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg.isEmpty() ? proxyClassNamePrefix + num : proxyPkg + "." + proxyClassNamePrefix + num; ...... /* * 生成代理类字节码 */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags); try { Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length, loader, null); reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE); return pc; } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } }
核心在于生成代理类字节码的
ProxyGenerator.generateProxyClass()
方法,有三个参数:name
: 代理类名称,$Proxy0interfaces
: 代理接口,数组accessFlags
:Modifier.PUBLIC | Modifier.FINAL
static byte[] generateProxyClass(ClassLoader loader, final String name, List<Class<?>> interfaces, int accessFlags) { ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); // 生成字节码文件 final byte[] classFile = gen.generateClassFile(); if (saveGeneratedFiles) { /* * 可以通过配置 jdk.proxy.ProxyGenerator.saveGeneratedFiles 参数 * 将字节码文件输出到本地文件 */ } return classFile; }
再跟进
ProxyGenerator.generateClassFile()
。private byte[] generateClassFile() { this.visit(58, this.accessFlags, dotToSlash(this.className), (String)null, "java/lang/reflect/Proxy", typeNames(this.interfaces)); // 添加 hashCode 方法 this.addProxyMethod(hashCodeMethod); // 添加 equals 方法 this.addProxyMethod(equalsMethod); // 添加 toString 方法 this.addProxyMethod(toStringMethod); Iterator var1 = this.interfaces.iterator(); while(var1.hasNext()) { Class<?> intf = (Class)var1.next(); Method[] var3 = intf.getMethods(); int var4 = var3.length; for(int var5 = 0; var5 < var4; ++var5) { Method m = var3[var5]; if (!Modifier.isStatic(m.getModifiers())) { // 添加代理接口方法 this.addProxyMethod(m, intf); } } } var1 = this.proxyMethods.values().iterator(); List sigmethods; while(var1.hasNext()) { sigmethods = (List)var1.next(); checkReturnTypes(sigmethods); } // 生成构造器 this.generateConstructor(); var1 = this.proxyMethods.values().iterator(); while(var1.hasNext()) { sigmethods = (List)var1.next(); Iterator var8 = sigmethods.iterator(); while(var8.hasNext()) { ProxyMethod pm = (ProxyMethod)var8.next(); this.visitField(10, pm.methodFieldName, "Ljava/lang/reflect/Method;", (String)null, (Object)null); pm.generateMethod(this, this.className); } } // 生成静态代码块 this.generateStaticInitializer(); return this.toByteArray(); }
ProxyGenerator
继承自ClassWriter
,通过 ASM 生成了字节码。这块就不细看了,具体 API 我也不是很清楚。挑一个生成构造器的generateConstructor()
方法看一下:private void generateConstructor() { MethodVisitor ctor = this.visitMethod(1, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", (String)null, (String[])null); ctor.visitParameter((String)null, 0); ctor.visitCode(); ctor.visitVarInsn(25, 0); ctor.visitVarInsn(25, 1); ctor.visitMethodInsn(183, "java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", false); ctor.visitInsn(177); ctor.visitMaxs(-1, -1); ctor.visitEnd(); }
可以看到确实是将
InvocationHandler
作为了构造器参数,但并不是直接给代理类生成的,而是Proxy
类。代理类是继承自Proxy
类,并引用父类的构造器。生成代理类字节码文件之后,通过
UnSafe.defineClass()
注册到 VM 。public Class<?> defineClass(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain) { if (b == null) { throw new NullPointerException(); } if (len < 0) { throw new ArrayIndexOutOfBoundsException(); } return defineClass0(name, b, off, len, loader, protectionDomain); } public native Class<?> defineClass0(String name, byte[] b, int off, int len, ClassLoader loader, ProtectionDomain protectionDomain);
提问环节
到这,动态代理的流程就跟完了。
经典提问环节:动态代理只能代理接口吗?如果是,为什么?
在评论区,留下你的答案吧!
转载自:https://juejin.cn/post/7134712273871831048