IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> jdk动态代理中的Proxy类与中间层InvocationHandler的深入研究总结 -> 正文阅读

[Java知识库]jdk动态代理中的Proxy类与中间层InvocationHandler的深入研究总结

一:jdk动态代理的逻辑介绍

动态生成的代理类里面每一个方法的实现都是调用InvocationHandler的invoke方法,在invoke()方法中,有三个参数,分别是Object,Method以及args数组,三个参数都是代理类Proxy调用时传入的。第一个参数Proxy传入的是当前的this对象;第二个参数Proxy传入的是需要被执行的Method对象;第三个参数则是Method对象需要的参数。因此,代理类只需要拿到对应的Method对象及相应参数,就可以通过InvocationHandler的invoke()方法对功能进行增强。代理类及Method对象都是jvm运行时通过反射生成的,因此jdk动态代理的底层就是反射

二:代理类是如何生成的

1.生成Object类的几个固有方法,包括toString(),hashCode(),equals()等

//代理类默认实现了被代理的接口以及继承了Proxy父类
//代理类在构造函数中,会把InvocationHandler实例交给父类Proxy
    public $proxy(InvocationHandler var1) throws  {
        super(var1);
    }
//代理类中的equals方法实现
public final boolean equals(Object var1) throws  {
    try {
????????//super.h.invoke()表示调用父类的h的invoke方法,m1则是要代理的方法,该方法我们
????????//会在InvocationHandler实例中,通过method.invoke()进行执行
        return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
    } catch (RuntimeException | Error var3) {
        throw var3;
    } catch (Throwable var4) {
        throw new UndeclaredThrowableException(var4);
    }
}
//代理类中获取被代理方法的method对象
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));

因此,需要传入类加载器与InvocationHandler实例

2.生成代理的方法

Proxy会拿到接口,遍历接口的所有方法,然后生成代理类的代理方法以及接口的Method对象,因此,生成代理类时,需要传入对应的接口参数。代理方法逻辑实现与上述逻辑一样,只是对象由Object变为了特定接口对象而已。

//代理接口方法,实质还是调用了InvocationHandler实例的invoke方法,只是参数method对象
//变成了该方法要代理的method对象m3
public final void toSchool() throws IOException {
    try {
        super.h.invoke(this, m3, (Object[])null);
    } catch (RuntimeException | IOException | Error var2) {
        throw var2;
    } catch (Throwable var3) {
        throw new UndeclaredThrowableException(var3);
    }

}

//拿到接口方法的method对象

m3 = Class.forName("test_procuration.Do").getMethod("toSchool");

因此真正执行逻辑是,代理类先执行自己的代理方法,进而调用InvocationHandler的invoke()方法,然后在invoke方法内,调用代理类传入的对应method对象,进而完成方法的代理。而method方法的执行者,则可以直接固定,一般指定为被代理对象,即被代理对象来执行该方法。因此,InvocationHandler需要持有被代理对象的引用。

(直接依赖接口或者接口实现类都可以,一般为了可扩展性,都会选择面向抽象,即让InvocationHandler内部依赖被代理接口,在外部传入接口实现类给InvovationHandler实例即可,因此需要代理同一接口的实现类时,若增强功能相同,代码是不需要变动的,只需要外部传递对应的接口实现类即可)

简而言之:生成的代理类会继承Proxy类以及实现执行的接口,从而完成接口方法的代理。代理方法会拿到父类Proxy的InvocatioinHandler实例(所以创建Proxy时要传入InvocationHandler实例),进而调用其invoke()方法。

三:几个有趣的坑

? ? ? ? 代理类与被代理类肯定不是同一个类,这毋庸置疑。但是如果直接输出两个对象会发现两个对象是同一个对象,哈希码也是相同的。但是通过“==”b比较两个对象却会返回false(证明不是同一个对象)。原因是输出被代理对象时,调用的是代理对象的toString()方法,根据代理逻辑,会去执行InvocationHandler的invoke()方法,进而执行方法里面的method.invoke()方法,问题就出现在这里,一般我们代理都是使用被代理对象去执行该方法的,即method.invoke(target,args),也就是使用target对象执行method的底层方法,完成方法的调用。即看似代理对象执行了toString()方法,实际是被代理对象执行了toString()方法,因此,无论怎么调用,拿到的都是被代理对象的hashCode。

? ? ? ? 那是不是把target换成是this,就可以执行this的方法,而前面说的该this是代理类传入的当前实例对象,即可以执行代理类自身的方法呢?其实如果换成this,是无法完成代理,生成无法完成对应mehod的调用的(method对象都不是自己的,自己都不是method对象对应的实例对象,如何调用?)

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-29 11:57:32  更:2022-04-29 12:00:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 1:55:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码