一:jdk动态代理的逻辑介绍
动态生成的代理类里面每一个方法的实现都是调用InvocationHandler的invoke方法,在invoke()方法中,有三个参数,分别是Object,Method以及args数组,三个参数都是代理类Proxy调用时传入的。第一个参数Proxy传入的是当前的this对象;第二个参数Proxy传入的是需要被执行的Method对象;第三个参数则是Method对象需要的参数。因此,代理类只需要拿到对应的Method对象及相应参数,就可以通过InvocationHandler的invoke()方法对功能进行增强。代理类及Method对象都是jvm运行时通过反射生成的,因此jdk动态代理的底层就是反射
二:代理类是如何生成的
1.生成Object类的几个固有方法,包括toString(),hashCode(),equals()等
//代理类默认实现了被代理的接口以及继承了Proxy父类
//代理类在构造函数中,会把InvocationHandler实例交给父类Proxy
public $proxy(InvocationHandler var1) throws {
super(var1);
}
//代理类中的equals方法实现
public final boolean equals(Object var1) throws {
try {
????????//super.h.invoke()表示调用父类的h的invoke方法,m1则是要代理的方法,该方法我们
????????//会在InvocationHandler实例中,通过method.invoke()进行执行
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
//代理类中获取被代理方法的method对象
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
因此,需要传入类加载器与InvocationHandler实例
2.生成代理的方法
Proxy会拿到接口,遍历接口的所有方法,然后生成代理类的代理方法以及接口的Method对象,因此,生成代理类时,需要传入对应的接口参数。代理方法逻辑实现与上述逻辑一样,只是对象由Object变为了特定接口对象而已。
//代理接口方法,实质还是调用了InvocationHandler实例的invoke方法,只是参数method对象
//变成了该方法要代理的method对象m3
public final void toSchool() throws IOException {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | IOException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
//拿到接口方法的method对象
m3 = Class.forName("test_procuration.Do").getMethod("toSchool");
因此真正执行逻辑是,代理类先执行自己的代理方法,进而调用InvocationHandler的invoke()方法,然后在invoke方法内,调用代理类传入的对应method对象,进而完成方法的代理。而method方法的执行者,则可以直接固定,一般指定为被代理对象,即被代理对象来执行该方法。因此,InvocationHandler需要持有被代理对象的引用。
(直接依赖接口或者接口实现类都可以,一般为了可扩展性,都会选择面向抽象,即让InvocationHandler内部依赖被代理接口,在外部传入接口实现类给InvovationHandler实例即可,因此需要代理同一接口的实现类时,若增强功能相同,代码是不需要变动的,只需要外部传递对应的接口实现类即可)
简而言之:生成的代理类会继承Proxy类以及实现执行的接口,从而完成接口方法的代理。代理方法会拿到父类Proxy的InvocatioinHandler实例(所以创建Proxy时要传入InvocationHandler实例),进而调用其invoke()方法。
三:几个有趣的坑
? ? ? ? 代理类与被代理类肯定不是同一个类,这毋庸置疑。但是如果直接输出两个对象会发现两个对象是同一个对象,哈希码也是相同的。但是通过“==”b比较两个对象却会返回false(证明不是同一个对象)。原因是输出被代理对象时,调用的是代理对象的toString()方法,根据代理逻辑,会去执行InvocationHandler的invoke()方法,进而执行方法里面的method.invoke()方法,问题就出现在这里,一般我们代理都是使用被代理对象去执行该方法的,即method.invoke(target,args),也就是使用target对象执行method的底层方法,完成方法的调用。即看似代理对象执行了toString()方法,实际是被代理对象执行了toString()方法,因此,无论怎么调用,拿到的都是被代理对象的hashCode。
? ? ? ? 那是不是把target换成是this,就可以执行this的方法,而前面说的该this是代理类传入的当前实例对象,即可以执行代理类自身的方法呢?其实如果换成this,是无法完成代理,生成无法完成对应mehod的调用的(method对象都不是自己的,自己都不是method对象对应的实例对象,如何调用?)
|