- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
首先,定义一个接口:
public interface Staff {
void work();
}
然后,新增一个类并实现上面的接口:
public class Coder implements Staff {
@Override
public 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;
}
@Override
public 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个概念,目标对象和代理对象, 。
目标对象是真正要调用的对象,上面示例中的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处重点:
先是获取构造函数,然后是生成代理类对象的实例.
思考一个问题,为什么 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又是单继承的,如果想要继续对类进行扩展,只能通过实现接口的方式.
文章持续更新,欢迎关注微信公众号「申城异乡人」第一时间阅读! 。
最后此篇关于【深度思考】聊聊JDK动态代理原理的文章就讲到这里了,如果你想了解更多关于【深度思考】聊聊JDK动态代理原理的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
有多久,没有发过短信了? 1、背景简介 在常规的分布式架构下,「消息中心」的服务里通常会集成「短信」的渠道,作为信息触达的重要手段,其他常用的手段还包括:「某微」、「某钉」、
区块链、低代码、元宇宙、AI智能; 01 【 先来说说背景 】 这个概念由来已久,但是在国内兴起,是最近几年; 低代码即「 Low-Code
目录 1、背景简介 2、订单业务 1、订单体系 2、流程管理 2
1. 相同点 用Python语言编写的源代码文件,其文件后缀是 “.py” 或 “.ipynb”。用Python语言编写的源代码文件,其文件后缀是 “.py” 或 “.ipynb”。 2. 区别
功能简介 闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态【CPJ 3.4.2】。闭锁的作用相当于一扇门∶ 在闭锁到达结束状态之前,这扇门一直是关闭的,并且没有任何线程能通过,当到达
高阶函数,英文叫 Higher Order function。一个函数可以接收另外一个函数作为参数,这种函数就叫做高阶函数。 示例: function add(x, 
引文 最近公司项目中使用了 Nuxt 框架,进行首屏的服务端渲染,加快了内容的到达时间 (time-to-content),于是笔者开始了对 Nuxt 的学习和使用。以下是从源码角度对 Nux
什么是游标? 游标(cursor)是一个存储在MySQL服务器上的数据库查询, 它不是一条SELECT语句,而是被该语句检索出来的结果集。在存储了游 标之后,应用程序可以根据需要滚动或浏
流水线工作模型在工业领域内十分常见,它将工作流程分为多个环节,每个环节根据工作强度安排合适的人员数量。良好的流水线设计尽量让各环节的流通率平衡,最大化提高产能效率。 Go 是一门实用性语言,流
1. Spring JDBC Spring JDBC的配置 2. Spring JdbcTemplate的常用方法 execute()
1. 前言 大家好,我是安果! 日常编写的 Python 自动化程序,如果在本地运行稳定后,就可以考虑将它部署到服务器,结合定时任务完全解放双手 但是,由于自动化程序与平台兼
前言 有时候我们会有在需要在网页中写代码或者改代码配置的需求,这个时候就需要用到代码编辑器,常规的代码编辑器有 CodeMirror 和 Monaco Editor, CodeMirror 使用的
前言:模块机制是 Node.js 中非常重要的组成,模块机制使得我们可以以模块化的方式写代码,而不是全部代码都写到一个文件里。我们平时使用的比较多的通过 require 加载模块,但是我们可能不
随着互联网的发展,越来越多的公司摒弃了Hibernate,而选择拥抱了MyBatis。而且,很多大厂在面试的时候喜欢问MyBatis底层的原理和源码实现。 总之,MyBatis几乎成为了Jav
@requestmapping和@getmapping @postmapping的区别 最近学习看一些代码,发现对于发送请求这件事,有的地方用@requestmapping,有的地方用@postm
@RequestParam 和 @PathVariable 注解是用于从request中接收请求的,两个都可以接收参数,关键点不同的是@RequestParam 是从request里面拿取值,而 @
PHP8的Alpha版本,过几天就要发布了,其中包含了不少的新特性,当然我自己认为最重要的还是JIT,这个我从2013年开始参与,中间挫折无数,失败无数后,终于要发布的东东。 不过,今天呢,我不打
引言 我们想要网格的服务发现、路由、熔断降级、负载均衡,这些流量治理都在数据面Envoy中执行才行。Envoy也提供的Filter机制来做这些功能,通常有以下方式: 通过C
我是一名优秀的程序员,十分优秀!