分析JDK的动态代理原理
一、什么是动态代理
动态代理的意思是通过生成代理对象来达到不改变源码的情况下,对代理的接口进行了方法的增强,主要的应用有Spring的AOP、事务管理等等
二、动态代理的原理
那动态代理的原理是什么呢,我们可以分析Proxy类的源码来进一步分析动态代理的原理,
首先就是代理对象的生成,是通过的Proxy类的的静态方法newProxyInstance生成的
此方法有三个参数,分别是类加载器,要代理的接口数组,实现了InvocationHandler接口的类的实例,进一步跟踪这个方法,这个方法主要做的事有先进行了权限检查,然后通过getProxyClass0方法生成了一个Class对象,而且通过这个Class对象获取Constructor对象,再使用Consturctor对象生成代理类的一个实例,即$Proxy0的一个实例
con是一个以constructorParams为参数的构造器对象,而constructorParams参数是InvocationHandler的class对象
进一步点进getProxyClass0方法进行跟踪,可以看到此方法主要是通过以下语句来获取Class对象的,
这个方法的意思是从proxyClass缓存里获取一个Class对象,这个方法挺长的,里面做的事情是看缓存里有没有我们需要的Class对象,没有则通过一系列的操作帮我们生成一个,而这个方法里面的关键语句是以下这个
可以点进apply方法,这个方法是Proxy的静态内部类ProxyClassFactory里的方法,这个方法主要做的事情有获取代理类的类名和包名
然后根据获取到的类名、要代理的接口数组和访问标志来生成一个字节码文件,即Class文件,实际上生成的是$Proxy0.class文件
最后根据类加载器,代理类类名,字节码文件和文件的长度生成一个Class对象,defineClass0是一个本地方法,就不进一步探究了
那么说完了原理,就举个栗子来看下具体是怎样使用的,首先新建一个要被代理的类,这个类实现了一个接下来要进行代理的接口
然后如果要进行代理的话就要新建一个实现了InvocationHandler接口的类,此类实现了InvocationHandler接口的invoke方法
invoke方法的三个参数是proxy:代理类的实例,method:被代理类或接口的Method实例,args:调用方法所需要的参数
不过在invoke方法里我们并没有用到proxy参数,这个参数的作用是让我们通过反射来获取代理对象的一些信息和可以返回一个代理对象的引用
最后就是测试代理的效果
通过Proxy.newProxyInstance来生成一个代理对象,并将它转型为Interface类型并把这个对象传给了Inteface引用,因为这样我们才能去调用被代理的方法,输出如下
可以看到虽然执行的是consumer方法,实际上调用的是实现了InvocationHandler接口的类里的invoke方法,那么问题又来了,invoke方法是怎样自动被调用的呢,通过输出可以看到proxy实例的类是
P
r
o
x
y
0
,
从
而
得
知
P
r
o
x
y
.
n
e
w
P
r
o
x
y
I
n
s
t
a
n
c
e
帮
我
们
生
成
的
是
Proxy0,从而得知Proxy.newProxyInstance帮我们生成的是
Proxy0,从而得知Proxy.newProxyInstance帮我们生成的是Proxy0类的一个实例,而通过前面的分析其实$Proxy0类是JVM根据我们所传入的代理类的类名和要代理的接口动态生成的
可以设置以下语句来生成
P
r
o
x
y
0.
c
l
a
s
s
从
而
分
析
Proxy0.class从而分析
Proxy0.class从而分析Proxy0类的源码
分析
P
r
o
x
y
0
类
的
源
码
可
以
看
到
Proxy0类的源码可以看到
Proxy0类的源码可以看到Proxy0类是继承了Proxy类并实现了我们要进行代理的Interface接口,而构造器是调用父类的
$Proxy0类有一个dosomething方法,此方法即是被代理的方法,这个方法里调用的是父类的InvocationHandler的invoke方法,proxy为this,即本类对象的引用,m3表示的如下图,调用m3不需要参数
dosomethingelse方法的原理也差不多,不过不同的是这个方法有一个字符串参数
$Proxy0类还代理了Object类里的equals,toString和hashCode方法
分析到这里我们就知道了invoke方法是怎样自动被调用的,例如调用了dosomething方法,而调用这个方法的是
P
r
o
x
y
0
类
的
实
例
,
即
调
用
的
是
Proxy0类的实例,即调用的是
Proxy0类的实例,即调用的是Proxy0类里的dosomething方法,dosomething方法里调用的是父类Proxy里的InvocationHandler实例的invoke方法,
所以达到了我们想要的代理的效果,我觉得这里也体现了多态性。