内容简介:动态代理与静态代理不同点在于,它可以动态生成任意个代理对象,无需要开发者手动编写代理类代码。loader(ClassLoader): 这个参数是实际被代理类的类加载器实例。
动态代理原理解析
1、原理结论阐述
动态代理与静态代理不同点在于,它可以动态生成任意个代理对象,无需要开发者手动编写代理类代码。 动态代理机制在运行时动态生成代理类字节码byte数组,然后通过jvm内部将字节码byte数组反序列化对应代理的Class对象,然后再通过反射机制创建代理类的实例 。
2、源码分析论证
-
1、第一步我们先从
Proxy.newProxyInstance方法进入探究,通过它在外部更为直观是可以获取代理类对象。
-
2、第二步进入
Proxy.newProxyInstance方法的定义
Proxy.newProxyInstance 有三个参数:
loader(ClassLoader): 这个参数是实际被代理类的类加载器实例。
interfaces(Class[]): 代理类和被代理类共同实现的接口的Class数组
h(InvocationHandler): 代理拦截器接口,一般需要使用子类去实现该接口或匿名类去实现
再一次来梳理下 newProxyInstance 源码流程:
首先传入 loader 、 interfaces 、 h 三个参数,先将 interfaces clone一份副本保存在 intfs 中,然后检查创建一个新的代理类所需要的权限,接着到了我们 第一个注意点1,就是通过 getProxyClass0 方法(需要传入 loader 和 intfs 参数)获得代理类的Class对象实例。拿到了代理类实例后,我们就通过反射的机制创建代理类实例 ;
到了我们的 注意点二,通过代理类Class对象 cl 获得构造器对象 cons ,并检查构造器对象是否是 public ,否则就强行修改访问权限 ;
最后到了 注意点三,通过 cons.newInstance 创建代理类对象,并且构造器反射中传入 h(InvocationHandler对象) ,说明我们可以推断一下生成的代理类中存在以 InvocationHandler 为参数的构造器 。
-
3、第三步进入
getProxyClass0方法,传入的参数loader和intfs,在该方法内部会委托给proxyClassCache的get方法, 如果给定的类加载器中定义的代理类实现了给定的接口,直接返回缓存中的副本,否则它将通过ProxyClassFactory创建代理类 .
-
4、第四步
proxyClassCache的介绍和定义, 请注意创建proxyClassCache传入的构造器两个参数分别是:KeyFactory和ProxyClassFactory
proxyClassCache 是一个 WeakCache<K,P,V> 对象, WeakCache<K,P,V> 中的 K 表示key值, P 代表参数, V 代表存储的值。此类用于缓存 (key,sub-key)->value 键值对。内部具体实现是借助了 ConcurentMap<Object,ConcurrentMap<Object,Supplier<V>>> , Supplier 是一个接口,就一个 get 方法用于获得值,不过是泛型 V 的包装类,第一个 Object 就是key(这里表达式不用泛型 K 是因为key值可以为null),第二个就是 sub-key ,那么它对应着什么呢? 而且具体的缓存中也没有泛型 P 呢,这就需要引出另外一个函数接口 BiFunction<T,U,R> ,该接口内部存在 R apply(T t,U u) 方法,这个方法意思就是根据传入两个泛型 T 和 U 的值经过一定计算得到泛型 R 的值。在 WeakCache<K,P,V> 类中存在两个BiFunction对象:
在 WeakCahe 类中只有一个核心 get 方法,里面包含了整个缓存的逻辑,注意我们获取代理类Class对象,就是通过 proxyClassCache.get(loader,interfaces); 实际上就是调用 WeakCache 中的 get 方法.
我们来一起梳理下 WeakCache 的逻辑: 首先 proxyClassCache 就是一个 WeakCache 实例对象,它有两个构造器参数 subKeyFactory 和 valueFactory ,创建 proxyClassCache 实例对应传入的是 proxyClassCache=newWeakCache<>(newKeyFactory(),newProxyClassFactory()) 中的 KeyFactory 和 ProxyClassFactory .
然后在 WeakCache 内部存在二级 ConcurrentHashMap , 一级map的 key 就是get方法传入的key, 通过这个key拿到 cacheKey ,从而拿到对应的 valuesMap二级map 。
然后又通过根据传入的一级map的 key 和参数 parameter , subKeyFactory 中的 apply 方法获得 sub-key ,通过 sub-key 拿到二级map中存储的 Supplier 对象,它可能是一个 CacheValue 也有可能是一个 Factory ,
最终通过 Factory 的 get 方法拿到实际的值。
对于上述有两个核心注意点
注意点1----->获取subKey过程:通过 subKeyFactory.apply(key,parameter) 拿到 sub-key
注意点2----> supplier.get()获取value的过程:
我们都知道 supplier 对应的可以是 Factory 对象,也就是最后会调用Factory中的 get 方法。
通过上述代码分析,我们知道最终value获取是来自于 valueFactory 中 apply 方法,还记得 valueFactory 是啥吗?没错它就是 ProxyClassFactory 也就是最终定位到了 ProxyClassFactory 中的 apply 方法。这也就是为什么之前说如果缓存中有直接从缓存中返回缓存的副本,没有就在ProxyClassFactory中创建代理对象。
-
5、第五步进入
ProxyClassFactory中的apply方法进行探究,这是创建新的代理类Class对象唯一来源。
再重新梳理一下 ProxyClassFactory 中的 apply 中的逻辑,首先做一些接口验证操作,然后通过 ProxyGenerator.generateProxyClass 生成确定的代理类Class文件的byte数组,最后通过defineClass0方法传入被代理类的类加载器、代理类唯一名称、生成的代理类文件反序列化成一个代理类的Class对象
-
6、第六步进入
ProxyGenerator中的generateProxyClass方法进行探究,主要通过它来生成代理类Class文件。generateProxyClass方法传入的参数主要有: proxyName(唯一代理类名称), interfaces(需要代理的接口Class数组), accessFlags(访问权限标识)
-
7、第七步进入
generateClassFile()方法,该方法主要生成Class文件。
-
8、以上就是整个动态代理中代理类生成代码的过程,为了进一步弄明白动态的机制,比如invoke是怎么调用的呢。不妨我们把生成代码保存在本地文件中,然后一起来看下生成的代理类长啥样。
其实当你看到了生成的代理类的代码后,你就会发现动态代理的机制就非常一目了然。你也就明白了 InvocationHandler 中的 invoke 方法什么时候调用了。那我们再来整体梳理下动态代理核心机制,其实最为核心的就是 InvocationHandler :
首先,我们需要去实现一个 InvocationHandler 的子类,重写它的 invoke 方法,该方法中会回调三个参数: Objectproxy,Methodmethod,Object[]args ,然后在我们在 invoke 方法中只需要通过调用 method 的 invoke 方法,并传入 args 参数。
然后我们去创建一个代理类实例是通过 Proxy.newProxyInstance ,会传入 InvocationHandler 子类实例,并把这个 InvocationHandler 子类实例作为生成新的代理类的构造器函数参数,并把这个参数传给新的代理类的父类 Proxy ,在 Proxy 中会维护这个 InvocationHandler 子类实例 h 。
然后通过上述生成的代理类代码来看,会把所有方法都转成对应的 Method 对象,并在静态初始化块中通过反射进行初始化,然后 每个方法内部调用实现,都会委托父类 Proxy 中的 h 中的 invoke 方法来实现调用,并把当前生成的代理类实例、当前方法对应的 Method 对象和参数数组 args 通过 invoke 回调出去,此时 InvocationHandler 子类中的 invoke 方法会得以触发 ,那么在其内部又转为 method 调用它的 invoke 方法,并传入 args 参数就相当于利用反射去调用这个方法。
最后到这里,有关动态代理内容就算说完了。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 设计模式——订阅模式(观察者模式)
- 设计模式-简单工厂、工厂方法模式、抽象工厂模式
- java23种设计模式-门面模式(外观模式)
- 设计模式-享元设计模式
- Java 设计模式之工厂方法模式与抽象工厂模式
- JAVA设计模式之模板方法模式和建造者模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
XML 在线格式化
在线 XML 格式化压缩工具
HEX CMYK 转换工具
HEX CMYK 互转工具