Java—>手写一个动态代理(原理篇)

Java基础

浏览数:168

2019-8-21

在自己手动来写一个动态代理前,我们先来了解一下什么静态代理:

先定义一个Person接口和两个实现类:

代理规则接口

被代理对象,实现Person代理规则

代理对象,在创建代理接口时,让其传一个代理目标,实现被代理者同样的接口,因为我们使用过代理者来去调用被代理者的方法,所以就得需要去实现与被代理者相同的接口

然后就没了,这就是静态代理,完全没难度的感觉….

优点:可以在不对我们的XiaoFang 这个类进行修改的前提下,对它进行功能的拓展

缺点:现在XiaoFang还只是想结婚而已,万一它以后想买车,买房,买船,买飞机…买各种它想要的东西的时候,咋办呢,难道每个都需要去写吗,因为代理对象,需要实现与目标对象同样的接口,这样会导致代理类非常多,这就很不爽了,维护起来就很蛋疼了,假如接口添加个方法,代理类跟被代理类都得去维护

动态代理:

原理: 在底层拿到被代理对象引用,然后获取接口,JDK从新生成一个类,同事这个类也是实现这个接口,把被代理对象的引用也拿到,然后编译这个类获取字节码

这里我们需要改变一下代理者的内部代码了

现在代理者实现的接口为InvocationHandler,稍后我们再来看看为什么要实现这个接口

使用的方式也变了

首先我们来看一下这个Person 到底是个什么对象

com.sun.proxy.$Proxy0   这里就会感觉很奇怪了,这特喵是个什么东西?

我们先看看这个对象里头到底是什么飞机

获取到生成类并且把它输出到磁盘

反编译后的$Proxy0,可以发现这个类,它也实现了我们的Person接口

这个地方很眼熟了吧

这个是我们关注的方法,重写我们的getLove方法

super.h.invoke(this, m3, (Object[])null);  是我们getLove方法中的执行代码,我们可以看到m3就是这个类中的变量,在静态代码块中给它赋值

这个反射的就是我们Person接口中的getLove方法

我们再来看看这个h 是个什么飞机

在Proxy类中,我们可以发现这个h,其实就是一个InvocationHandler而我们在写代理者的时候就是得实现这个InvocationHandler接口

是不是突然明白了点什么东西,用InvocationHandler的invoke方法,我们再来看看这个invoke方法

第一个是一个对象,在$Proxy0  中,是把$Proxy0 自己,跟那个m3,还有一个null给传了进来

Proxy.newProxyInstance(XiaoFang.class.getClassLoader(),XiaoFang.class.getInterfaces(),matchmaker); 

当时我们是这么做来获取到这个$Proxy0的,现在我们来看看这里面是搞什么飞机

我们在Proxy.newProxyInstance时 传的matchmaker,也就是代理对象 h

看到这里我一眼就看到了这个getProxyClass0这个玩意,还有上面的注释,Look up or generate the designated proxy class.

这里首先是做了一个判断,判断我们被代理类所实现的接口数,又学到了一个玩意,65535,其实$Proxy0这个类就是在这个proxyClassCache的get方法中生成的,这个proxyClassCache是一个存放代理类缓存的地方,其实我们的$proxy0是动态生成的类,而proxyClassCache就是保存这种生成的类的地方,所以他会先去里头找,找到了就直接返回这个对象,找不到则通过ProxyClassFactory来创建这个类,有兴趣大家可以自己去研究这个地方我就不搞得太详细了(因为我快下班了)

再往下可以发现,获取了这个代理类的构造函数对象,并且把h给赋值给了另外一个对象,还记得h是什么吗,h就是我们在newProxyInstance的给这个方法传的代理对象就是那个Matchmaker

最后就为这个$proxy0 创建了一个对象,并且返回;

所以我们在调用这个getlove时候流程是 先调用了$proxy的getlove方法

然后再有我们在newProxyInstance时传的matchmaker 上图就变成了:

matchmaker.invoke($proxy0,me3,null);

最后还是通过我们在创建代理对象时传的被代理对象来执行的getlove方法

作者:haloSky_life