文章目录

  • 1. 示例
  • 2. 原理
  • 3. 为什么必须要基于接口?

1. 示例

首先,定义一个接口:

public interface Staff {void work();
}

然后,新增一个类并实现上面的接口:

public class Coder implements Staff {@Overridepublic void work() {System.out.println("认真写bug……");}
}

假设现在有这么一个需求:在不改动以上类代码的前提下,对该方法增加一些前置操作或者后置操作。

接下来就来讲解下,如何使用JDK动态代理来实现这个需求。

  • 首先,自定义一个调用处理器,实现java.lang.reflect.InvocationHandler接口并重写invoke方法:
public class AttendanceInvocationHandler implements InvocationHandler {private final Object target;public AttendanceInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("上班打卡……");Object invoke = method.invoke(target, args);System.out.println("下班打卡……");return invoke;}
}

重点看下Object invoke = method.invoke(target, args);,该行代码会执行真正的目标方法,在这前后,我们可以添加一些增强逻辑。

  • 然后,新建个测试类,看下JDK动态代理如何使用:
public class JdkProxyTest {public static void main(String[] args) {Coder coder = new Coder();AttendanceInvocationHandler h = new AttendanceInvocationHandler(coder);// 创建代理对象Object proxyInstance = Proxy.newProxyInstance(coder.getClass().getClassLoader(),coder.getClass().getInterfaces(),h);Staff staff = (Staff) proxyInstance;staff.work();}
}
  • 运行以上代码,效果如下图所示:

从运行结果可以看出,在目标方法的前后,执行了自定义的操作。

2. 原理

这里理解2个概念,目标对象和代理对象

  • 目标对象是真正要调用的对象,上面示例中的Coder类就是目标对象,
  • 代理对象是JDK自动生成的对象,在代理对象内部会去调用目标对象的目标方法。

JDK动态代理的核心就是上面示例中的Proxy.newProxyInstance方法,方法签名如下图所示:

第1个参数传入的是目标对象的ClassLoader,第2个参数传入的是目标对象的接口信息,第3个参数传入的是自定义的InvocationHandler

然后看下该方法的实现逻辑,先看第1处重点:

注释翻译过来是:查找或者生成指定的代理类。

该方法会生成代理类的字节码文件(也可能是从缓存中读取),核心逻辑在ProxyClassFactory类的apply方法中,

该方法中定义了生成的代理类的包名以及文件名:

因此默认情况下,自动生成的代理类名称是com.sun.proxy.$Proxy0。
该方法最后会生成代理类的字节码,默认情况下不会保存到文件系统,但可以通过参数指定保存到文件系统:

可以看出,保存不保存到文件系统,受saveGeneratedFiles的影响,其定义如下所示:

private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));

所以可以通过指定sun.misc.ProxyGenerator.saveGeneratedFiles参数来让生成的代理类字节码文件保存到文件系统中。
然后看第2处重点:

先是获取构造函数,然后是生成代理类对象的实例。

3. 为什么必须要基于接口?

思考一个问题,为什么JDK动态代理必须要基于接口,带着这个问题,我们看下动态生成的代理类com.sun.proxy.$Proxy0长什么样子?
JVM参数里添加参数-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true,然后启动上面示例中的测试代码:

生成的代理类字节码文件保存在项目根目录下的com/sun/proxy目录下:

在IDEA中打开后,如下图所示:

在静态代码块中,对静态变量m0、m1、m2、m3进行了赋值,其中m3是要执行的目标方法。
在构造方法中,执行的是super(var1);,也就是父类Proxy的构造方法:

该方法是将我们自定义的InvocationHandler赋值给了父类的变量h。
而以下测试代码实际执行的是代理类$Proxy0里的work方法:

Staff staff = (Staff) proxyInstance;
staff.work();

代理类$Proxy0里的work方法实际执行的是自定义InvocationHandler里的invoke方法:

因此在执行目标方法前后,执行了自定义的前置操作和后置操作。

了解了这个调用过程,就理解了为什么JDK动态代理必须要基于接口,因为动态生成的代理类已经继承了类java.lang.reflect.Proxy,而Java又是单继承的,如果想要继续对类进行扩展,只能通过实现接口的方式。

【java】JDK动态代理原理相关推荐

  1. Java - JDK动态代理原理

    Java - JDK动态代理原理 前言 一. JDK动态代理源码分析 1.1 生成目标代理类 getProxyClass0 1.1.1 KeyFactory 生成接口的虚引用 1.1.2 ProxyC ...

  2. 08.jdk动态代理原理

    课程标题<jdk动态代理底层原理分析> 课程内容: 1.什么是代理模式 2.代理模式应用场景有哪些 3.代理模式实现方式有哪些 4.静态代理与动态代理区别 5.JDK动态代理原理分析 6. ...

  3. 深入理解JDK动态代理原理,使用javassist动手写一个动态代理框架

    文章目录 系列文章索引 一.动手实现一个动态代理框架 1.初识javassist 2.使用javassist实现一个动态代理框架 二.JDK动态代理 1.编码实现 2.基本原理 (1)getProxy ...

  4. java jdk动态代理学习记录

    转载自: https://www.jianshu.com/p/3616c70cb37b JDK自带的动态代理主要是指,实现了InvocationHandler接口的类,会继承一个invoke方法,通过 ...

  5. Java JDK 动态代理实现和代码分析

    JDK 动态代理 内容 一.动态代理解析 1. 代理模式 2. 为什么要使用动态代理 3. JDK 动态代理简单结构图 4. JDK 动态代理实现步骤 5. JDK 动态代理 API 5.1 java ...

  6. java jdk动态代理 cglib动态代理demo

    最近在研究java动态代理这块,以前也看了很多次java动态代理,感觉一直不是怎么明白,这两天看了看又明白了些,现给出我参考网上写的一个demo jdk动态代理实现: View Code import ...

  7. Java JDK 动态代理

    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代 ...

  8. 【Java】动态代理原理分析

    动态代理的常用实现方式是反射. 动态代理的常用实现方式是反射. 动态代理的常用实现方式是反射. 反射机制是指程序在运行期间可以访问.检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类 ...

  9. Java设计模式(五)代理设计模式—静态代理—JDK动态代理—Cglib动态代理

    文章目录 什么是代理模式 代理模式应用场景 代理的分类 静态代理 什么是静态代理 深入解析静态代理 小结 动态代理 什么是动态代理 JDK动态代理 原理和实现方式 代码实现 优缺点 Cglib动态代理 ...

最新文章

  1. 记录一下Pycharm习惯的快捷键
  2. 3位格雷码的顺序编码_绝对值编码器当中的格雷码
  3. MySQL之SELECT查询表达式
  4. Nodejs连接mysql的增、删、改、查操作
  5. resnet过拟合_重读经典:完全解析特征学习大杀器ResNet
  6. Java高级工程师必看系列,已拿到offer
  7. c语言编写程序求8,使用c语言编写程式,实现计算1*2*3+4*5*6+7*8*9+……+28*29*30的值...
  8. Linux:守护进程详解及实现
  9. a blog about grid stytem
  10. 新闻发布系统数据库设计
  11. 偶极子阵列天线(带反射板)+单层天线罩
  12. 毕马威it咨询 java_【毕马威(KPMG)工资】it咨询师待遇-看准网
  13. python3爬取torrent种子链接实例
  14. Python简单模拟微信发红包
  15. Autoar之 CAN NM网络管理
  16. 微软发布了免费的文件恢复工具!
  17. 博弈论(Game Theory)
  18. 【写在七夕浪浪漫时刻】Go中遇到http code 206和302的获取数据的解决方案
  19. linux系统浏览器最小化,调整Firefox火狐浏览器的最小化、最大化、关闭按钮键大小...
  20. 系统里有哪些软中断?

热门文章

  1. 2021清远市清城中学高考成绩查询,清远市清城中学中考成绩
  2. 启动windows服务时出现“错误1053:服务没有及时响应启动或控制请求”的解决方法
  3. 软件测试周刊(第59期):觉得自己生活的很好吗?为什么视而不见?
  4. Ubuntu 10.04 下载(官方地址)
  5. 【网络篇】TCP SYN Flood Attack(洪水攻击)
  6. 重装win7系统显示:无法安装在gpt硬盘上
  7. MethodHandle.invoke and MethodHandle.invokeExact are only supported starting with Android O
  8. SpringBoot+Vue实现前后端分离的餐饮点餐系统
  9. el-date-picker修改为周类型日期插件时间格式是开始时间字段级和结束时间字段两个传参给后台
  10. 互联网五层、七层结构