动态代理-JDK 源码剖析(二):源码剖析

来源:互联网 发布:学乐器的软件 编辑:程序博客网 时间:2024/06/06 00:27

从示例可以看出,动态代理是基于实现类的接口和实现类对象,首先构建代理对象(与实现类有共同的接口),之后通过接口调用来实现代理。

那么,动态代理的关键就是分析如何根据接口和类对象来生成代理对象。

生成代理类过程主要分为生成代理类,以及代理类实例化对象两个步骤。

在Proxy.newProxyInstance函数中,传入的有ClassLoader loader,Class[] interfacecs,InvocationHandler h三个参数。Proxy.newProxyInstance包含了代理类生成的两个步骤(分别是getProxyClass0(loader,interfaces)和newInstance(cons,ih)。

首先进入代理类生成过程:

1、           相关准备工作:校验接口个数不能超过65535,接口interfaceName数组,以及interfaceSet数组(校验重复接口),以及加载相关的接口的类(Class.forName(interfaceName,false,loader)。【相关参数校验

2、           创建class Loader的代理类缓存,用于避免多次创建代理类(采用两级锁机制),最终将并发创建代理类的多线程中只允许其他一个线程完成代理类的构造,其他线程等待该线程完成代理类的构造。【并发创建控制

3、           针对非public类型的代理接口,记录其包名,那么代理类将会在相同的包中生成(对于多个非public的interface【在不同的包名】,会报错“non-public interfaces from different packages”的IllegalArgumentException异常),如果是public类型的代理接口,那么包名默认是“com.sun.proxy.”。【确认包名,考虑非Public的interface的访问范围】

4、           确定代理类的名称,有个并发访问安全的证书递增Long类型的数据结合“$proxy”作为类名,例如$proxy1。【确认类名

5、           通过ProxyGenerator.generateProxyClass(proxyName,interfaces)来生成类字节数组。

6、           通过definClass0(loader,proxyName,proxyClassfile,0,proxyClassFile.length)方法来创建代理类。并将对应的proxyClass类放在proxyClasses结构中,这样通过proxyClasses可以确定类是否是代理类。

7、           在try catch finnaly结构的finnaly结构体中,如果生成了proxyClass,会将proxyClass的虚引用放在加载类的代理类缓存中(cache.put(key,newWeakReferner<class ?>(proxyClass)),创建好后,会通知第二步在等待的并发创建线程。

【在上面的步骤中,我们将考虑几个问题】

1)       ProxyGenerator.generateProxyClass方法是如何构建代理类文件内容的?

首先是根据proxyClasName和interfaces来构建ProxyGenerator对象(默认会初始化hashCodeMethod,equalsMethod和toStringMethod属性[赋予Object的hashCode,equals和toString方法])。

进入generateClassFile()方法中,将hashCode,equals,toString三个方法先添加上。之后,将每个接口中的方法添加到代理类中。addProxyMethod方法调用方式见后面。

之后,由于每个函数字符串可以对应于多个代理类函数。

因此,当出现多个代理类函数时,需要校验每个代理函数的返回类型(checkReturnTypes方法)。

生成代理类构造函数generateConstructor,并添加到methods属性中。

并将接口中的全部代理函数信息在fields和methods中全部注册。

校验代理(methods,fields)个数限制(16的四次方,共65536)。添加

ProxyGenerator.ConstantPool. ProxyGenerator.ConstantPool

到了这一步,就剩下如何构建class文件。

2)           definClass0函数是如何生成代理类?

这里是native类,需要进一步的根据natvie的方法内容进行处理。

3)           为什么将需引用放在加载类的代理类缓存中?

运行期创建目标对象的代理非常耗时,使用缓存来存储生成的代理类显得尤为重要。jdk动态代理使用弱引用指向cache中的代理类,以便代理类对象能够被GC回收。

原创粉丝点击