【代理模式】了解篇:静态代理 动态代理~
目录
1、什么是代理模式?
2、静态代理
3、动态代理
3.1 JDK动态代理类
3.2 CGLIB动态代理类
4、JDK动态代理和CGLIB动态代理的区别?
1、什么是代理模式?
定义:
代理模式就是为其他对象提供一种代理以控制这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象就可以在客户端和目标对象之间起到中介的作用。类似于租房:租户和房东之间现在不直接沟通,找了中介来联系,那么中介就是代理,这种模式就是代理模式。
代理模式分为静态代理和动态代理。

2、静态代理
在静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不方便(比如接口一旦增加新方法,目标对象和代理对象都要进行修改)同时也麻烦(需要对于每个目标类都单独写一个代理类,类似于房东有很多套房子,每套房子都要找一个中介来代理),因此实际场景中静态代理的使用非常少。
(1)从JVM的层面上讲:静态代理是在编译的时候就将接口、实现类、代理类等都变成了一个个实际的class文件。
模拟租户和房东之间的租房业务:
静态代理的使用步骤:
(1)创建一个接口和实现类——相当于房主现在要出租房;
//定义接口
public interface SellHouseService {void sell();
}
不过房主有很多套房子,现在只想出租房子一;
//实现接口:具体的房东要出租房子
public class SellHouseOneService implements SellHouseService{@Overridepublic void sell() {System.out.println("SellHouseService...");}
}
(2)创建一个代理类也实现这个接口 ——房主将房子一出租的任务交给中介,不然中介也没有权限去给租户看房子。
//创建代理类
public class StaticProxy implements SellHouseService{//将目标对象注入到代理类(),然后在代理类的对应方法调用目标类中的对应方法private final SellHouseService sellHouseService;public StaticProxy(SellHouseService sellService){this.sellHouseService = sellService;}@Overridepublic void sell() {System.out.println("before...");sellHouseService.sell();System.out.println("after...");}
}
(3)主函数
public class Main {public static void main(String[] args) {//创建目标对象:房东SellHouseService service = new SellHouseOneService();//创建代理对象:中介SellHouseService proxy = new StaticProxy(service);proxy.sell();}
}

当我租第二套房子,第三套的时候也要找一个代理...非常麻烦。这个时候我们就使用动态代理了。
3、动态代理
相比于静态代理,动态代理更加灵活,我们不用再针对每个目标类都单独去创建一个代理类了。
小Tips
(1)动态代理主要有两种:JDK动态代理和CGLIB动态代理机制。
(2)其中,Spring AOP的实现就依赖了动态代理。
(3)从JVM角度讲:动态代理是在运行的时候动态生成类字节码,并加载到JVM中。
(4)在Java动态dialing机制中:InvocationHander接口和Proxy类是核心。
3.1 JDK动态代理类
JDK动态代理类的使用步骤:
(1)定义接口及实现类(同静态代理)
(2)自定义InvocationHandler并重写invoke方法,在invoke方法中调用被代理类的方法并定义一些处理逻辑;
//自定义InvokeHandler并重写invoke方法
public class JDKInvocationHandler implements InvocationHandler {//目标对象就是被代理对象private Object target;public JDKInvocationHandler(Object target){this.target = target;}//代理对象,要运行的方法,传递的实参@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//增强方法:前置处理//1、安全检查System.out.println("安全检查");//2、记录日志System.out.println("记录日志");//3.时间统计开始System.out.println("记录开始时间");//4、在invoke方法中会通过反射调用被代理类的方法Object retVal = method.invoke(target,args);//5、时间统计结束System.out.println("记录结束时间");return retVal;}
}
(3)创建一个代理类并使用。
public class Main {public static void main(String[] args) {//创建代理对象SellHouseService target = new SellHouseOneService();//创建一个代理类:通过被代理类、被代理实现的接口、方法调用处理器来创建SellHouseService proxy = (SellHouseService) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{SellHouseService.class},new JDKInvocationHandler(target));proxy.sell();}
}

小Tips
问:如何为Java对象创建一个代理对象?
java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法;
(1)Public static Object newProxyInstance(ClassLoader loader,Class<?> [ ] interfaces,InvocationHandler h),newProxyInstance作用是生成一个代理对象。返回值就是代理;
(2)这个方法一共有三个参数:loader类加载器,用于加载代理对象;interfaces:被代理类实现的一些接口;还有实现了InvocationHanldler接口的对象。
(3)JDK动态代理的问题:只能代理实现了接口的类:因此使用CGLIB动态代理来解决。
(4)通过
Proxy类的newProxyInstance()创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler接口的类的invoke()方法。 你可以在invoke()方法中自定义处理逻辑,比如在方法执行前后做什么事情。
3.2 CGLIB动态代理类
CGLIB(Code Generation Library)允许我们在运行的时候对字节码进行修改和动态生成。CGLIB允许我们通过继承的方式实现代理。CGLIB动态代理机制中MethodIntercepor接口和Enhancer类是核心。在Spring AOP的设计中,如果目标对象实现了接口,则默认采用JDK动态代理,否则使用CGLIB动态代理。
CGLIB动态代理类的使用步骤:
(1)添加依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>
(2)自定义MethodInterceptor并重写Intercept方法,intercept用于拦截增强被代理类的方法,和JDK动态代理中的Invoke方法类似;
public interface MethodInterceptor extends Callback{// 拦截被代理类中的⽅法public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
}
public class CGLIBInterceptor implements MethodInterceptor {//被代理对象private Object target;public CGLIBInterceptor(Object target){this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {//1.安全检查System.out.println("安全检查");//2.记录⽇志System.out.println("记录⽇志");//3.时间统计开始System.out.println("记录开始时间");//通过cglib的代理⽅法调⽤Object retVal = methodProxy.invoke(target, args);//4.时间统计结束System.out.println("记录结束时间");return retVal;}
}
(3)通过Enhancer类的create()创建代理类并使用。
public static void main(String[] args) {SellHouseService target= new SellHouseOneService();SellHouseService proxy= (SellHouseService) Enhancer.create(target.getClass(),new CGLIBInterceptor(target));proxy.sell();}

4、JDK动态代理和CGLIB动态代理的区别?
(1)JDK 动态代理只能代理实现了接⼝的类或者直接代理接⼝,⽽ CGLIB 可以代
理未实现任何接⼝的类。
(2)CGLIB 动态代理是通过⽣成⼀个被代理类的⼦类来拦截被代理类的⽅法调
⽤,因此不能代理声明为 final。(3)性能: ⼤部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更
加明显

相关文章:
【代理模式】了解篇:静态代理 动态代理~
目录 1、什么是代理模式? 2、静态代理 3、动态代理 3.1 JDK动态代理类 3.2 CGLIB动态代理类 4、JDK动态代理和CGLIB动态代理的区别? 1、什么是代理模式? 定义: 代理模式就是为其他对象提供一种代理以控制这个对象的访问。在某…...
LLM 大语言模型 Prompt Technique 论文精读-3
WebShop: Towards Scalable Real-World Web Interaction with Grounded Language Agents 面向可扩展的基于语言引导的真实世界网络交互 链接:https://arxiv.org/abs/2207.01206 摘要:现有的用于在交互环境中引导语言的基准测试要么缺乏真实世界的语言元…...
架构重构实践心得
一、前言 大多数的技术研发都对重构有所了解,而每个研发又都有自己的理解。从代码重构到架构重构,我参与了携程大型全链路重构项目,积累了一点经验心得,在此抛砖引玉和大家分享。 二、重构的定义 重构是指在不改变外部行为的情…...
【配置环境】Windows下 VS Code 远程连接虚拟机Ubuntu
一,环境 Windows 11 家庭中文版VMware Workstation 16 Pro (版本:16.1.2 build-17966106)ubuntu-22.04.2-desktop-amd64 二,关键步骤 Windows下安装OpenSSHVS Code安装Remote - SSH插件 三,详细步骤 在Ubun…...
【设计模式——学习笔记】23种设计模式——组合模式Composite(原理讲解+应用场景介绍+案例介绍+Java代码实现)
案例引入 学校院系展示 编写程序展示一个学校院系结构: 需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院,一个学院有多个系 【传统方式】 将学院看做是学校的子类,系是学院的子类,小的组织继承大…...
vue3+Luckysheet实现表格的在线预览编辑(electron可用)
前言: 整理中 官方资料: 1、github 项目地址https://github.com/oy-paddy/luckysheet-vue-importAndExport/tree/master/https://github.com/oy-paddy/luckysheet-vue-importAndExport/tree/master/ 2、xlsx vue3 json数据导出excel_vue3导出excel_羊…...
前端html中让两个或者多个div在一行显示,用style给div加上css样式
文章目录 前言一、怎么让多个div在一行显示 前言 DIV是层叠样式表中的定位技术,全称DIVision,即为划分。有时可以称其为图层。DIV在编程中又叫做整除,即只得商的整数。 DIV元素是用来为HTML(标准通用标记语言下的一个应用&#x…...
【linux基础(二)】Linux基本指令(中)
💓博主CSDN主页:杭电码农-NEO💓 ⏩专栏分类:Linux从入门到开通⏪ 🚚代码仓库:NEO的学习日记🚚 🌹关注我🫵带你学更多操作系统知识 🔝🔝 Linux基本指令 1. 前言2. 删除…...
ceph集群---使用RBD块存储
文章目录 创建和删除池RBD设备的配置及使用RBD 块设备数据的导出和导入 块存储接口是一种主流的存储访问接口,也是常见的存储形态,比如服务器下的/dev/sdx都是块存储设备。你可以像使用磁盘一样来使用Ceph提供的块存储设备。 在创建块存储设备之前&#…...
2022.09.17【读书笔记】丨生物信息学与功能基因组学(第十三章 蛋白质结构预测 下)
目录 蛋白质结构预测三种方法同源建模(比较建模)穿线法从头预测(ab initio)基于假设推荐策略 精度与方法选择Alphafold2相关信息 蛋白质结构预测 三种方法 同源建模(比较建模) 建模4步骤 1.模板选择和确定折叠构象 通过blast或delta-blast搜索同源蛋白…...
ardupilot获取飞行员目标倾斜角度
目录 文章目录 目录摘要1. 4.0.7获取目标倾斜角度2. 4.3.7获取目标倾斜角度3.仿真摘要 本节主要记录ardupilot获取目标倾斜角度的两种实现方法,主要针对4.0.7和4.3.7进行对比。 1. 4.0.7获取目标倾斜角度 1.姿态模式下获取函数 //获取飞行员期望的倾斜角度get_pilot_desire…...
机器人制作开源方案 | 智能垃圾桶
1. 功能说明 智能垃圾桶是一种利用物联网技术和智能感知能力的智能设备,旨在提高垃圾分类和处理的效率。通常具备以下特点和功能: ① 智能感知:智能垃圾桶配备各种传感器,如压力传感器、红外线传感器等,可以实时感知…...
【手撕】list
系列文章目录 文章目录 系列文章目录前言list_node<T>(节点)_list_iterator<T, Ref, Ptr>(迭代器)成员变量构造函数运算符重载 List<T>(链表)成员变量构造函数析构函数区间构造函数拷贝构…...
QMQTT快速入门
文章目录 QMQTT快速入门环境搭建mosquitto 服务器和客户端配置服务器配置客户端配置模拟MQTT的发布订阅 QMQTT - Windows下的客户端项目代码展示遇到的问题 QMQTT快速入门 环境搭建 准备一台linux设备和一台windows设备虚拟机也是可以的;安装mosquitto ࿱…...
Dooring-Saas低代码技术详解
hello, 大家好, 我是徐小夕, 今天和大家分享一下基于 H5-Dooring零代码 开发的全新零代码搭建平台 Dooring-Saas 的技术架构和设计实现思路. 背景介绍 3年前我上线了第一版自研零代码引擎 H5-Dooring, 至今已迭代了 300 多个版本, 主要目的是快速且批量化的生产业务/营销过程中…...
Linux chmod
chmod 首先chmod 用于修改文件权限,使用命令 ll 查看文件列表,或者使用stat文件名 可以查看其相应的权限 显示的形式为例如 rwx r- - r-- ,即所有者拥有读写执行的权限 ,同组人员和其他人都只拥有读的权限 一般修改权限为三部分…...
java商城系统和php商城系统有什么差异?如何选择?
java商城系统和php商城系统是两种常见的电子商务平台,它们都具有一定的优势和劣势。那么,java商城系统和php商城系统又有哪些差异呢? 一、开发难度 Java商城系统和PHP商城系统在开发难度方面存在一定的差异。Java商城系统需要使用Java语言进…...
【HTML】常用实体字符(如 nbsp; 空格)
文章目录 显示结果描述实体名称实体编号空格 <小于号<<>大于号>>&和号&"引号" ’撇号' (IE不支持)¢分(cent)¢¢£镑(pound)£ £元&…...
华为eNSP通过VMnet8虚拟网卡,NAT转换访问互联网
防火墙上配置: 配置G1/0/1接口IP地址,其实G1/0/1的IP就是终端PC1的网关地址。 配置G1/0/0接口自动获取IP地址,从VMnet8中自动获取地址。 配置安全区域zone,把对应的接口加入到对应的zone中 配置安全策略,放通trust安全区域到u…...
手撕顺序表
> 作者简介:დ旧言~,目前大一,现在学习Java,c,c,Python等 > 座右铭:松树千年终是朽,槿花一日自为荣。 > 望小伙伴们点赞👍收藏✨加关注哟💕…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅
目录 前言 操作系统与驱动程序 是什么,为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中,我们在使用电子设备时,我们所输入执行的每一条指令最终大多都会作用到硬件上,比如下载一款软件最终会下载到硬盘上&am…...
