动态代理
概述
之前在实现静态代理时,需要根据实现的接口来自己去定义生成代理类。而 JDK 动态代理运用了 java.lang.reflect.Proxy 类中的方法,想要生成一个类的代理类,只需要自定义一个处理方法执行前后逻辑的 handler,调用 Proxy 类中的生成代理类的方法,就可以得到代理类了,不需要针对不同的接口去自己编写代理类了。
举例
定义代理类和被代理类共同实现的接口
interface Human {
String getBelief();
void eat(String food);
}
定义被代理类 SuperMan
class SuperMan implements Human {
@Override
public String getBelief() {
return "I believe I can fly!";
}
@Override
public void eat(String food) {
System.out.println("I like to eat " + food);
}
}
创建一个通用的代理类生产工厂,传入一个对象,可以返回一个它的代理类
class ProxyFactory {
// 调用此方法,返回一个代理类的对象,解决1.
public static Object getProxyInstance(Object obj) {
// obj: 被代理类的对象
Object instance = Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),
// 这是实现InvocationHandler接口的自定义类,是一个函数接口
// 里面只有invoke方法
(proxy, method, args) -> {
System.out.println("方法调用之前......");
// 通过反射调用被代理对象的方法
Object o = method.invoke(obj, args);
System.out.println("方法调用之后......");
return o;
});
return instance;
}
}
newProxyInstance() 中的最后一个参数是一个实现了 InvocationHandler 接口的对象。InvocationHandler 是一个函数接口,具体源码如下
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
当代理类的方法被调用时,代理类就会调用 handler 的 invoke 方法来执行相关逻辑。这样,我们就可以在被代理类执行方法(method.invoke(obj, args)
)前后来织入我们自己想要执行的逻辑。
测试
public class ProxyTest {
@Test
public void test() {
SuperMan superMan = new SuperMan();
// 获取代理对象
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
// 调用接口的方法,会自动调用handler中的invoke方法,动态调用同名方法
proxyInstance.eat("apple");
}
}
执行结果
可见在执行被代理对象方法前后,也执行了自定义的逻辑
源码
源码分为两部分,分别是 Proxy 生成代理类的方法和生成好的代理类执行目标方法
注:本文源码对应的 JDK 版本为 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(); /* * 找到用于生成代理类的构造方法 */ Constructor<?> cons = getProxyConstructor(caller, loader, interfaces); return newProxyInstance(caller, cons, h); }
关键语句是倒数第二个语句,进入这个方法
-
Proxy.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); } // 向代理cache插入新的proxyBuilder return proxyCache.sub(intf).computeIfAbsent( loader, (ld, clv) -> new ProxyBuilder(ld, clv.key()).build() ); } else { // 对接口数组进行深拷贝 final Class<?>[] intfsArray = interfaces.clone(); if (caller != null) { checkProxyAccess(caller, loader, intfsArray); } // 将数组转换为list final List<Class<?>> intfs = Arrays.asList(intfsArray); return proxyCache.sub(intfs).computeIfAbsent( loader, (ld, clv) -> new ProxyBuilder(ld, clv.key()).build() ); } }
在 Proxy 类中有个属性 proxyCache,这是一个装有代理类构造器的缓存。第 17 行中,ld 是 class loader,clv 是 class loader value,在这里 clv 的 key 就是被代理类实现的接口 Human
ProxyBuilder 的匿名对象 new 出来后,进入他的
build()
方法 -
Proxy.ProxyBuilder.build()
方法/** * 生成代理类的Class类并返回要求的构造器 */ Constructor<?> build() { // 定义代理类的Class类 Class<?> proxyClass = defineProxyClass(module, interfaces); final Constructor<?> cons; try { 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; }
第 6 行就是生成代理类 Class 类的语句,进入这个方法
-
java.lang.reflect.Proxy.ProxyBuilder#defineProxyClass
/** * 生成代理类的Class类 */ private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) { String proxyPkg = null; // 代理类所属的package int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * 记录所有非public接口的package,来保证后面生成的代理类和非public接口所在的package是 * 一样的。同时也为了验证所有非public的接口都在同一个package下(如果不在一个package, * 生成的代理类就无法访问这些接口) */ 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) { // 如果所有接口都是public,那么就用默认的package proxyPkg = m.isNamed() ? PROXY_PACKAGE_PREFIX + "." + m.getName() : PROXY_PACKAGE_PREFIX; } else if (proxyPkg.isEmpty() && m.isNamed()) { // 如果package名是个空字符串且module不是unnamed,抛出异常 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()); } } /* * 生成代理类的名字 */ // 由于要生成新的代理类,全局代理类序号加一 long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg.isEmpty() ? proxyClassNamePrefix + num : proxyPkg + "." + proxyClassNamePrefix + num; ClassLoader loader = getLoader(m); trace(proxyName, m, loader, interfaces); /* * 生成代理类 */ // 生成代理类的字节码 byte[] proxyClassFile = ProxyGenerator.generateProxyClass(loader, proxyName, interfaces, accessFlags); try { // 根据生成的字节码定义Class类 Class<?> pc = JLA.defineClass(loader, proxyName, proxyClassFile, null, "__dynamic_proxy__"); // 向代理类缓存中添加这个代理类 reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE); return pc; } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } }
首先先确定所有的非 public 的接口是否都在一个包下,以确保后面生成的代理类也在这个包下(否则就无法访问这些接口了)。然后生成代理类的名字,这里代理类的名字的生成规则是代理类统一前缀+序号,这个序号每要生成一个代理类,就用 cas 操作加一。
第 59 行就是生成代理类字节码的语句,调用的是 ProxyGenerator 类的方法,先看看这个类的类图
它继承了 ClassWriter,这里判断它具有生成类字节码文件的功能
-
java.lang.reflect.ProxyGenerator#generateProxyClass
static byte[] generateProxyClass(ClassLoader loader, final String name, // 之前生成的代理类名字 List<Class<?>> interfaces, int accessFlags) { // new一个代理类生成器 ProxyGenerator gen = new ProxyGenerator(loader, name, interfaces, accessFlags); // 生成代理类的字节码文件 final byte[] classFile = gen.generateClassFile(); // ... return classFile; }
进入第 8 行的语句调用的方法
-
java.lang.reflect.ProxyGenerator#generateClassFile
private byte[] generateClassFile() { visit(V14, accessFlags, dotToSlash(className), null, JLR_PROXY, typeNames(interfaces)); /* * 向代理类添加一些通用方法:hashCode, equals, toString * 这步在接口方法生成之前执行,是因为如果接口方法有重写这三个Object类里的方法 * 就可以实现覆盖 */ addProxyMethod(hashCodeMethod); addProxyMethod(equalsMethod); addProxyMethod(toStringMethod); /* * 将所有接口的所有方法加进去 */ for (Class<?> intf : interfaces) { for (Method m : intf.getMethods()) { if (!Modifier.isStatic(m.getModifiers())) { addProxyMethod(m, intf); } } } /* * 检查同名同参方法返回类型是否有冲突 */ for (List<ProxyMethod> sigmethods : proxyMethods.values()) { checkReturnTypes(sigmethods); } // 生成构造方法 generateConstructor(); for (List<ProxyMethod> sigmethods : proxyMethods.values()) { for (ProxyMethod pm : sigmethods) { // 对代理类中的Method对象添加static修饰符 visitField(Modifier.PRIVATE | Modifier.STATIC, pm.methodFieldName, LJLR_METHOD, null, null); // 生成代理类执行这个方法的代码 pm.generateMethod(this, className); } } // 生成类的静态代码块,用来初始化那些静态Method对象 generateStaticInitializer(); return toByteArray(); }
首先添加默认的 hashCode, equals, toString 三个方法,然后再添加接口的所有方法(实际上是给代理类添加了 Method 对象)。然后生成构造器。由于之前添加方法只是给类添加了 Method 成员变量(之后会给出示例),将这几个变量添加静态修饰符,还需要给代理类生成方法的代码(有代码才能执行)。最后加上静态代码块,用于初始化那几个静态 Method 对象。
-
回到第 4 步代码的 60 行。得到字节码后,生成代理类 Class 类,然后向缓存中添加这个代理类。
-
回到第 3 步,得到代理类后,得到它的构造器,返回即可
-
回到第 2 步,返回得到的构造器
-
回到第 1 步的最后一句,执行
newProxyInstance()
方法,其实就是执行以下语句return cons.newInstance(new Object[]{ h});
这个构造器只有一个入参,就是之前自定义的 invoke 方法的 handler
生成的字节码文件反汇编
final class $Proxy0 extends Proxy implements Human {
// 之前提到的Method对象
private static Method m0;
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
// 构造器,入参是InvocaitonHandler类型的
public $Proxy0(InvocationHandler param1) {
super(var1);
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{
var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String getBelief() {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void eat(String var1) {
try {
super.h.invoke(this, m4, new Object[]{
var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("example.Human").getMethod("getBelief");
m4 = Class.forName("example.Human").getMethod("eat", Class.forName("java.lang.String"));
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可见,代理类中有静态成员变量,都是 Method 的类型,用于方法被调用时,将这些 Method 对象传入 handler。这些静态 Method 会在静态代码块中被初始化,通过反射的方式获得这些方法。当一个方法被调用是,将这个代理类、对应的 Method 对象以及相应的参数传入 handler 即可。
总结
JDK 动态代理主要分为以下几个步骤
- 根据传入的被代理类的类加载器和其实现的接口,生成代理类的字节码文件。包括创建接口方法和 3 个 Object 类的方法的 Method 对象(这些对象命名规范为 m0, m1, m2, …)作为代理类的静态属性、生成方法对应的代码、生成构造器、生成初始化静态属性的静态代码块
- 字节码生成完成后,执行构造方法,将用户定义的实现了 InvocationHandler 接口的 handler 对象传入代理类的构造方法中
- 执行代理类的某一方法时,代理类调用 handler 的 invoke 方法,其中传入的参数包括目标方法对应的代理类中的 Method 对象(比如在本文中的字节码反汇编代码中,eat 方法对应 m4),完成方法调用和执行