当Kotlin邂逅设计模式之代理模式-下篇(二)

栏目: 后端 · 发布时间: 4年前

内容简介:动态代理与静态代理不同点在于,它可以动态生成任意个代理对象,无需要开发者手动编写代理类代码。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 为参数的构造器

当Kotlin邂逅 <a href='https://www.codercto.com/topics/17995.html'>设计模式</a> 之代理模式-下篇(二)

  • 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 方法拿到实际的值。

当Kotlin邂逅设计模式之代理模式-下篇(二)

对于上述有两个核心注意点

注意点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 参数就相当于利用反射去调用这个方法。

最后到这里,有关动态代理内容就算说完了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

SRE

SRE

贝特西 拜尔 (Betsy Beyer)、等 / 孙宇聪 / 电子工业出版社 / 2016-10-1 / CNY 108.00

大型软件系统生命周期的绝大部分都处于“使用”阶段,而非“设计”或“实现”阶段。那么为什么我们却总是认为软件工程应该首要关注设计和实现呢?在《SRE:Google运维解密》中,Google SRE的关键成员解释了他们是如何对软件进行生命周期的整体性关注的,以及为什么这样做能够帮助Google成功地构建、部署、监控和运维世界上现存最大的软件系统。通过阅读《SRE:Google运维解密》,读者可以学习到......一起来看看 《SRE》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具