Java中的动态代理(JDK Proxy VS CGLib)
前言
动态代理可以说是Java基础中一个比较重要的内容,这块内容关系到Spring框架中的AOP实现原理,所以特别写了一篇作为个人对这块知识的总结。这部分内容主要包括:JDK Proxy和CGLib的基本介绍、二者的实现原理、代码示例等。
什么是动态代理
动态代理是指程序在运行过程中,动态代理的实现是基于某个特点类而生成的增强类,也就是说,动态代理生成的类会顶替掉原来的类而被执行从而做到达到增强的目前
目前流行的动态代理机制有两种:JDK Proxy 和 CGLIB
这两个人实现动态代理的方式不同,前者是要求被代理类必须继承接口,而后者则没有这个要求,因为他是通过生成代理类的子类来进行构造的。
JDK Proxy
JDK Proxy 是Java官方提供的一种动态代理类实现方式,它的实现动态代理特点如下:
- 代理类实现接口
- 通过反射生产代理类
为什么被代理类要实现接口
可能有人会有疑惑为什么说JDK Proxy仅支持实现了接口的被代理类,这个问题需要分几点来回答
【首先】生成的代理类是通过反射的方式构建的,这个过程中,这个代理类可以认为就是被代理类的“克隆体”,但是与之不同的其中一个点的是,这个生产的代理类也需要继承Proxy才行,但是假如被代理类继承了其他类,但是Java中是不允许多继承的
,所以被代理类是不能继承其他类,否则就会出现多继承的错误!!!
【其次】在Java中实现接口和继承类是不冲突的,为了在生产代理类后,我们可以拿到被代理类的同名方法然后进行重写(重写内容后续说明),所以JDK Proxy选择了实现接口的这种方式
代码示例
以下是动态代理的一个代码示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 定义接口
interface UserService {void save();
}// 实现接口的具体类
class UserServiceImpl implements UserService {public void save() {System.out.println("Save user");}
}// 实现InvocationHandler接口来自定义代理逻辑
class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method");Object result = method.invoke(target, args);System.out.println("After method");return result;}
}public class Main {public static void main(String[] args) {UserService userService = new UserServiceImpl();// 创建InvocationHandler对象MyInvocationHandler handler = new MyInvocationHandler(userService);// 通过Proxy类的静态方法创建代理对象UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),userService.getClass().getInterfaces(),handler);// 调用代理对象的方法proxy.save();}
}
在这段代码中
【1】我们先看接口和被代理类,实现比较简单,接口实现类重写接口方法。
【2】MyInvocationHandler是一个接口实现类,它的目的是为了创建自定义的代理规则逻辑,target属性就是被代理类,MyInvocationHandler的构造方法必须将被代理对象的引用传递进来,然后进行属性赋值
【3】MyInvocationHandler中的invoke()方法是最核心的,它才是实现动态代理规则逻辑的主要部分
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method");Object result = method.invoke(target, args);System.out.println("After method");return result
}
在这个方法中,method.invoke这个语句就是我们被代理类的方法执行,这个方法中通过传递被代理类target,在内部调用这个被代理类的方法,然后在metho.invoke方法上下就可以执行其他前置后置增强逻辑,也就是说,我们生成的动态代理类的同名方法是通过实现前置逻辑和后置逻辑+被代理类的同名方法
来实现动态代理,这样既不会丢失原本逻辑也可以做大增强!!!
【4】在main方法中
proxy.save()
这个方法其实即是动态代理生成的方法,也就是MyInvocationHandler中的invoke方法
图解(逻辑)
CGLIB
CGLIB是非官方创建的动态代理库。它通过继承被代理类来创建代理类。
CGLib不需要被代理类实现接口,它能够代理普通的类。CGLib通过修改字节码来实现代理。
代码示例
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 被代理类
class RealSubject {public void doSomething() {System.out.println("RealSubject doSomething");}
}// 代理类
class ProxySubject implements MethodInterceptor {private Object target; // 被代理对象public Object getInstance(Object target) {this.target = target;Enhancer enhancer = new Enhancer();enhancer.setSuperclass(this.target.getClass());enhancer.setCallback(this);return enhancer.create();}public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method: " + method.getName());Object result = proxy.invokeSuper(obj, args);System.out.println("After method: " + method.getName());return result;}
}public class CGLIBDynamicProxyExample {public static void main(String[] args) {ProxySubject proxy = new ProxySubject();RealSubject realSubject = (RealSubject) proxy.getInstance(new RealSubject());realSubject.doSomething();}
}
【1】在这段代码中,我们会发现在ProxySubject类中的getInstance方法也需要传入被代理类target,但与JDK 不同的是,这个方法内部是为了获取他的class类,然后生成一个代理类继承自被代理类。
【2】与JDK Proxy中类似,也有一个类似于invoke方法的intercept方法,他内部执行的proxy.invokeSuper(obj,args)
,其实就是调用父类的目标方法,也就是被代理类的方法
【3】在main方法中,我们会发现其创建代理类的方式和JDK Proxy很相似,都需要把代理规则类放入到代理工厂中进行创建,最后创建出来的对象就是被代理对象的子类
图解
相关文章:

Java中的动态代理(JDK Proxy VS CGLib)
前言 动态代理可以说是Java基础中一个比较重要的内容,这块内容关系到Spring框架中的AOP实现原理,所以特别写了一篇作为个人对这块知识的总结。这部分内容主要包括:JDK Proxy和CGLib的基本介绍、二者的实现原理、代码示例等。 什么是动态代理…...
Redis 7 第七讲 哨兵模式(sentinal)
哨兵模式 哨兵巡查监控后台master主机是否故障,如果出现故障根据投票时自动将某一个从库转换成新的主库,继续对外服务。 作用 1. 监控redis运行状态,包括master和slave 2. 当master down机,能自动将salve切换成新的master 应用场景 主从监控监控主从redis库运行的状态…...

Python入门教程 - 判断语句(二)
目录 一、布尔类型 二、比较运算符 三、if判断语句 一、布尔类型 True False result1 10 > 5 result2 10 < 5 print(result1) print(result2) print(type(result1)) True False <class bool> 二、比较运算符 ! > < > < 比较运算的结果是布尔…...
LeetCode-55-跳跃游戏-贪心
题目描述: 给你一个非负整数数组 nums ,你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标,如果可以,返回 true ;否则,返回 false 。 解…...

【USRP】调制解调系列4:BPSK、QPSK、8PSK、OQPSK、Pi/4DQPSK,基于labview的实现
PSK Phase Shift Keying – 相移键控 在某些调制解调器中用于数据传输的调制系统,在最简单的方式中,二进制调制信号产生0和1。载波相位来表示信号占和空或者二进制1和O。对于有线线路上较高的数据传输速率,可能发生4个或8个不同的相移&…...

深入探讨梯度下降:优化机器学习的关键步骤(一)
文章目录 🍀引言🍀什么是梯度下降?🍀损失函数🍀梯度(gradient)🍀梯度下降的工作原理🍀梯度下降的变种🍀随机梯度下降(SGD)🍀批量梯度下降…...

layui框架学习(40:数据表格_主要事件)
Layui数据表格模块主要通过各类事件响应工具栏操作、单元格编辑或点击等交互操作,本文学习table数据表格模块中的主要事件及处理方式。 头部工具栏事件。通过代码“table.on(‘toolbar(test)’, function(obj))”获取lay-filter属性为test的数据表格的头部工具栏…...

kotlin实现猜数游戏
游戏规则 1.程序随机生成一个1到100的数字,作为MagicNumber 2.用户根据提示输入数据,只有三次机会输入数据 代码 代码很简单,使用了let内置函数 fun main() {//生成随机数可以使用java的方法//val magicNumber Random().nextInt(11)val ma…...

51单片机项目(8)——基于51单片机的DS1302时钟系统
本次做的设计,是利用DS1302时钟芯片,做了一个时钟,并且将实时时间通过串口发送到上位机进行显示。系统运行如下:(protues文件和相关keil代码会在文章最后给出!!!) DS1302…...
高频策略:做市商与逆向选择
参与交易市场的三类人: 出于某种现实的需要而进行交易的人。例如投资者买入股票等金融资产长期持有,是为了使自己当前的资产进行升值,获得比银行利息更高的收益;制造业公司为了锁定生产成本而进行对冲交易。对于这些人来说&#…...
Valgrind内存诊断工具的使用笔记
Valgrind是一款用于内存调试、内存泄漏检测以及性能分析的神器,能够帮助我们快速定位到程序的内存问题,比如内存泄漏导致的 段错误 (核心已转储) 包含以下工具: Memcheck:检查程序中的内存问题,如泄漏、越界、非法指…...

docker安装Nacos
哈喽!大家好,我是旷世奇才李先生 文章持续更新,可以微信搜索【小奇JAVA面试】第一时间阅读,回复【资料】更有我为大家准备的福利哟,回复【项目】获取我为大家准备的项目 文章目录 一、Nacos是什么1、简介2、功能1、服务…...

【Linux】线程安全-死锁
文章目录 死锁问题场景1场景2死锁的gdb调试造成死锁的必要条件不可剥夺循环等待互斥条件请求和保持 预防死锁破坏必要条件,循环等待&请求和保持加锁顺序一致避免锁没有被释放资源一次性分配 死锁问题 死锁的两种场景: 场景1 线程加锁之后一直没有将锁…...

pdf转换成图片免费软件用哪个?pdf转换成图片就用它
随着技术的发展,现在企业办公运用到的电子文档各种各样,我们日常需要掌握的技能越来越高要求,其中pdf和图片是我们经常接触的文件格式之一,而且这两个文件格式我们会经常将它们进行转换,那么pdf转换成图片怎么操作呢?…...

【LeetCode】《LeetCode 101》第十二章:字符串
文章目录 12.1 字符串比较242 . 有效的字母异位词(简单)205. 同构字符串(简单)647. 回文子串(中等)696 . 计数二进制子串(简单) 12.2 字符串理解224. 基本计算器(困难&am…...
Android去掉视频声音
【Android】使用MediaExtractor、MediaMuxer去掉视频文件中的音频数据_android 去掉视频音频_little_fat_sheep的博客-CSDN博客 void removeSound() {try {String path Environment.getExternalStorageDirectory().getPath();String filename "no_sound_" input_p…...
java-thread-affinity线程绑核
通过将线程绑定到指定的cpu上,可以提高执行效率。因为每次都是相同的cpu,可以充分利用高速缓存,在java中可以使用以下依赖来使用。 <dependency><groupId>net.openhft</groupId><artifactId>affinity</artifactId><ver…...
Springboot - 5.test集成
👀1. 简介 spring-boot-starter-test是Spring Boot框架中的一个模块,用于支持在项目中进行单元测试和集成测试。它提供了一些依赖项和工具,使得编写和运行测试变得更加方便。以下是关于spring-boot-starter-test的全面介绍: ✌1…...

弯道超车必做好题集锦三(C语言编程题)
目录 前言: 1.单词倒排 方法1:scanf匹配特定字符法 方法2: 双指针法 2.统计每个月兔子的总数 方法1:斐波那契数列 方法2:斐波那契的递归 3.珠玑妙算 方法:遍历 4.寻找奇数(单身狗&#…...

JavaScript基础语法03——JS注释、结束符
哈喽,大家好,我是雷工! 今天继续学习JavaScript基础语法知识,注释和结束符,以下为学习笔记。 一、JavaScript注释 JavaScript注释有什么作用? JavaScript注释可以提高代码的可读性,能够帮助像…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...