内容简介:动态代理与静态代理不同点在于,它可以动态生成任意个代理对象,无需要开发者手动编写代理类代码。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设计模式之模板方法模式和建造者模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。