当前位置: 首页 > news >正文

代理模式介绍(静态代理、jdk动态代理、cglib代理)

一、静态代理

(一)定义

1、定义

为其他对象提供一种代理以控制对这个对象的访问;

2、涉及到的角色

(1)抽象主题角色:真实主题和代理主题的共同接口,便于在使用真实主题的地方都可以使用代理主题;
(2)代理主题角色:代理类,负责控制对真实主题的引用,在需要的时候创建和删除真实主题,并且在真实主题处理完毕后做预处理和善后处理的工作;
(3)真实主题角色:被代理角色,业务逻辑的具体执行者;

(二)类图

在这里插入图片描述

(三)代码实现

1、抽象主题角色(业务接口)
package com.xiaobai.design_pattern.proxy;/*** @author wangtw* @date 2023/12/1 23:03* @description 代理模式-抽象主题角色*/
public interface IGamePlayer {/*** 打怪*/void killBoss();/*** 升级*/void upGrade();
}
2、真实主题角色(具体业务功能)
package com.xiaobai.design_pattern.proxy;/*** @author wangtw* @date 2023/12/1 23:04* @description 代理模式-真实主题角色*/
public class GamePlayer implements IGamePlayer{/*** 打怪角色*/private String name;public GamePlayer(String name) {this.name = name;}public String getName() {return name;}/*** 打怪*/@Overridepublic void killBoss() {System.out.println(this.name + "在打野怪");}/*** 升级*/@Overridepublic void upGrade() {System.out.println(this.name + "升了1级");}
}
3、代理主题角色(代理类)
package com.xiaobai.design_pattern.proxy;import java.lang.reflect.Field;
import java.util.Date;/*** @author wangtw* @date 2023/12/1 23:10* @description*/
public class GamePlayerProxy implements IGamePlayer{private IGamePlayer gamePlayer;public GamePlayerProxy(IGamePlayer gamePlayer) {this.gamePlayer = gamePlayer;}private void start() {System.out.println("开始时间为" + new Date());}private void end() {System.out.println("结束时间为" + new Date());}/*** 获取角色名称* @param operateStep*/private void operatePerson(String operateStep) {Class<? extends IGamePlayer> aClass = gamePlayer.getClass();Field[] declaredFields = aClass.getDeclaredFields();for (Field declaredField : declaredFields) {if (declaredField.getName().equals("name")) {declaredField.setAccessible(true);try {Object name = declaredField.get(gamePlayer);System.out.println(name + operateStep + "操作");} catch (IllegalAccessException e) {e.printStackTrace();}}}}@Overridepublic void killBoss() {operatePerson("开始");start();this.gamePlayer.killBoss();}@Overridepublic void upGrade() {this.gamePlayer.upGrade();operatePerson("结束");end();}
}
4、测试类
package com.xiaobai.design_pattern.proxy;import org.junit.jupiter.api.Test;/*** @author wangtw* @date 2023/12/1 23:21* @description 代理模式测试类*/
public class ClientDemo {@Testpublic void test() {IGamePlayer player = new GamePlayer("芈月");IGamePlayer proxy = new GamePlayerProxy(player);proxy.killBoss();proxy.upGrade();}
}

输出:

芈月开始操作
开始时间为Sat Dec 02 20:14:09 CST 2023
芈月在打野怪
芈月升了1级
芈月结束操作
结束时间为Sat Dec 02 20:14:09 CST 2023

(四)总结

1、代理模式的优点:
(1)职责清晰:真实角色负责处理实际的业务逻辑,不用关心非本职的事务,通过代理完成附加的事务;
(2)高扩展性:不同的需求可能会有不同的真实角色,只要实现了接口,代理类就可以完全在不做任何修改的情况下代理各种真实主题角色;
2、静态代理模式的缺点:
(1)若抽象主题角色增加功能,会影响代理类;
(2)不同的功能需求可能会有不同的代理类;

二、jdk动态代理

(一)使用动态代理的场景

在程序执行期间,使用jdk的反射机制创建代理类对象,动态指定要代理的对象;
不依赖于业务接口;
静态代理目标很多时,可以使用动态代理,避免静态代理的缺点;

(二)代码实现

1、创建代理类

代理类需要实现InvocationHandler接口,并重写invoke方法,生成代理类对象后,调用目标类实现方法时会调用生成代理对象中的invoke方法

package com.xiaobai.design_pattern.proxy;import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;/*** @author wangtw* @date 2023/12/2 21:36* @description jdk动态代理处理器*/
public class JdkGamePlayerProxy implements InvocationHandler {private Object targetObject;public JdkGamePlayerProxy(Object object) {this.targetObject = object;}private void start() {System.out.println("开始时间为" + new Date());}private void end() {System.out.println("结束时间为" + new Date());}/*** 获取角色名称* @param operateStep*/private void operatePerson(String operateStep) {Class<?> aClass = this.targetObject.getClass();Field[] declaredFields = aClass.getDeclaredFields();for (Field declaredField : declaredFields) {if (declaredField.getName().equals("name")) {declaredField.setAccessible(true);try {Object name = declaredField.get(this.targetObject);System.out.println(name + operateStep + "操作");} catch (IllegalAccessException e) {e.printStackTrace();}}}}/**** @param proxy 代理对象* @param method 方法* @param args 方法参数* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("============开始执行" + method.getName() + "方法==============");operatePerson("开始");start();Object invoke = method.invoke(this.targetObject, args);operatePerson("结束");end();System.out.println("============结束执行" + method.getName() + "方法==============");return invoke;}
}
2、测试方法

使用java.lang.reflect.Proxy#newProxyInstance生成代理对象

@Test
public void jdkProxyTest() {//生成代理类文件 在根目录的同级目录,com下System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");IGamePlayer player = new GamePlayer("澜");Class<? extends IGamePlayer> playerClass = player.getClass();//代理类 实现需要实现InvocationHandler接口,重写invoke方法 传入业务实现类对象JdkGamePlayerProxy jdkGamePlayerProxy = new JdkGamePlayerProxy(player);// 创建代理类对象/***loader – the class loader to define the proxy classinterfaces – the list of interfaces for the proxy class to implementh – the invocation handler to dispatch method invocations to*/IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(playerClass.getClassLoader(),playerClass.getInterfaces(), jdkGamePlayerProxy);System.out.println(proxy.getClass());proxy.killBoss();proxy.upGrade();
}

在这里插入图片描述
输出:

class com.sun.proxy.$Proxy9
开始执行killBoss方法==
澜开始操作
开始时间为Sat Dec 02 23:27:05 CST 2023
澜在打野怪
澜结束操作
结束时间为Sat Dec 02 23:27:05 CST 2023
结束执行killBoss方法==
开始执行upGrade方法==
澜开始操作
开始时间为Sat Dec 02 23:27:05 CST 2023
澜升了1级
澜结束操作
结束时间为Sat Dec 02 23:27:05 CST 2023
结束执行upGrade方法==

jdk动态代理依赖于java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy

三、cglib动态代理

(一)说明

1、说明

cglib动态代理需要依赖第三方,以下代码使用spring生态的cglib包(org.springframework.cglib)实现cglib代理;

2、基础类介绍:

org.springframework.cglib.proxy.MethodInterceptor:方法拦截器类;
org.springframework.cglib.proxy.Enhancer:增强类;
org.springframework.cglib.proxy.MethodProxy:方法代理类

(二)代码实现

拦截器需要实现org.springframework.cglib.proxy.MethodInterceptor接口,重写intercept方法,使用org.springframework.cglib.proxy.MethodProxy#invoke执行目标方法

1、拦截器实现
package com.xiaobai.design_pattern.proxy;import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;/*** @author wangtw* @date 2023/12/3 20:17* @description*/
public class CglibGamePlayerProxy implements MethodInterceptor {private Object targetObject;public CglibGamePlayerProxy(Object targetObject) {this.targetObject = targetObject;}private void start() {System.out.println("开始时间为" + new Date());}private void end() {System.out.println("结束时间为" + new Date());}/*** 获取角色名称* @param operateStep*/private void operatePerson(String operateStep) {Class<?> aClass = this.targetObject.getClass();Field[] declaredFields = aClass.getDeclaredFields();for (Field declaredField : declaredFields) {if (declaredField.getName().equals("name")) {declaredField.setAccessible(true);try {Object name = declaredField.get(this.targetObject);System.out.println(name + operateStep + "操作");} catch (IllegalAccessException e) {e.printStackTrace();}}}}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("============开始执行" + method.getName() + "方法==============");operatePerson("开始");start();Object result = methodProxy.invoke(this.targetObject, objects);operatePerson("结束");end();System.out.println("============结束执行" + method.getName() + "方法==============");return result;}
}
2、测试方法

使用org.springframework.cglib.proxy.Enhancer对目标类进行增强,并设置回调拦截器

@Test
public void cglibProxyTest() {IGamePlayer gamePlayer = new GamePlayer("亚瑟");// 对目标方法进行增强Enhancer enhancer = new Enhancer();enhancer.setSuperclass(IGamePlayer.class); // 设置要代理的父类enhancer.setCallback(new CglibGamePlayerProxy(gamePlayer)); // 设置回调的拦截器IGamePlayer proxy = (IGamePlayer) enhancer.create(); // 创建代理对象proxy.killBoss();proxy.upGrade();
}

输出:

开始执行killBoss方法==
亚瑟开始操作
开始时间为Sun Dec 03 20:47:39 CST 2023
亚瑟在打野怪
亚瑟结束操作
结束时间为Sun Dec 03 20:47:39 CST 2023
结束执行killBoss方法==
开始执行upGrade方法==
亚瑟开始操作
开始时间为Sun Dec 03 20:47:39 CST 2023
亚瑟升了1级
亚瑟结束操作
结束时间为Sun Dec 03 20:47:39 CST 2023
结束执行upGrade方法==

参考:

韩敬海 设计模式(Java版)
Java-JDK动态代理
JDK动态代理
动态代理之 cglib 实现

相关文章:

代理模式介绍(静态代理、jdk动态代理、cglib代理)

一、静态代理 &#xff08;一&#xff09;定义 1、定义 为其他对象提供一种代理以控制对这个对象的访问&#xff1b; 2、涉及到的角色 &#xff08;1&#xff09;抽象主题角色&#xff1a;真实主题和代理主题的共同接口&#xff0c;便于在使用真实主题的地方都可以使用代理…...

设计模式基础——工厂模式剖析(2/2)

目录 一、工厂模式 1.1 工厂模式的定义 1.2 工厂模式的设计意图 1.3 工厂模式主要解决的问题 1.4 工厂模式的缺点 1.5 实际的应用案例 1. 数据库连接池 2. 图形用户界面&#xff08;GUI&#xff09;组件 3. 文件操作 二、各种工厂模式的变形 1.1 简单工厂模式&#…...

spark3.x 读取hudi报错

报错信息如下: Exception in thread "main" org.apache.hudi.exception.HoodieUpsertException: Failed to upsert for commit time 20231201203145254 at org.apache.hudi.table.action.commit.BaseWriteHelper.write(BaseWriteHelper.java:64) at org.apa…...

微信小程序中block和View组件的使用区别

block和View组件都是用于布局的组件: 1. Block组件&#xff1a; Block组件是一个无实际显示效果的组件&#xff0c;它主要用于包裹一组组件&#xff0c;并提供了类似于div的作用。使用Block组件可以将一组组件进行分组&#xff0c;便于样式的管理和控制。Block组件不会在页面…...

代码混淆技术探究与工具选择

代码混淆技术探究与工具选择 引言 在软件开发中&#xff0c;保护程序代码的安全性是至关重要的一环。代码混淆&#xff08;Obfuscated code&#xff09;作为一种常见的保护手段&#xff0c;通过将代码转换成难以理解的形式来提升应用被逆向破解的难度。本文将介绍代码混淆的概…...

selenium 解决 id定位、class定位中,属性值带空格的解决办法

一、前置说明 selenium遇到下面这种元素&#xff1a; <th id"demo id" class"value1 value2 value3 ">1、虽然id一般不会有空格&#xff0c;但是前端错误的这种写法(如下图)&#xff0c;会造成使用id定位不到元素&#xff0c;如&#xff1a; find…...

gma 空间绘图实战(1):绘制多个子图,连接并展示局部放大区域

安装 gma&#xff1a;pip install gma 本文基于&#xff1a;gma 2.0.3&#xff0c;Python 3.10 本文用到的矢量数据为&#xff1a;CTAmap 1.12。来源于 https://www.shengshixian.com/ 。&#xff08;感谢锐多宝&#xff09; 绘图目标 参考代码 import matplotlib.pyplot as p…...

Unity中C#使用协程控制Shader材质变化

文章目录 前言一、协程是什么二、在Unity中使用协程1、我们在 Start 中测试一下协程的执行顺序2、我们实现一个点击按钮实现角色受击效果 三、协程中的动画过渡1、首先&#xff0c;在协程内实现中毒并且消散的效果2、在 OnGUI 内&#xff0c;给一个新按钮使用刚刚定义的协程 四…...

WordPress禁止显示指定类别的文章

使用wordpress禁止输出指定类别的文章可以给get_posts()函数传个数组参数&#xff0c;如下&#xff1a; <div class"widget" id"diary1"> <h3>随机呈现</h3> <ul> <?php $argsarray( numberposts>16, category>-9,-12, …...

C#里面的泛型(T),泛型类,泛型方法,泛型接口等简单解释

https://blog.csdn.net/dap769815768/article/details/81946506 只是比较简单的解释&#xff0c;在实际使用中&#xff0c;如果遇到需要深入研究的场景&#xff0c;再翻阅相关资料深入研究下。 一、泛型T 这个T在实际使用中很常见&#xff0c;比如List<T>。其实我们还…...

C语言——指针(五)

&#x1f4dd;前言&#xff1a; 上篇文章C语言——指针&#xff08;四&#xff09;更加深入的介绍了不同类型指针的特点&#xff0c;这篇文章主要想记录一下函数与指针的结合运用以及const和assert关于指针的用法&#xff1a; 1&#xff0c;函数与指针 2&#xff0c;const 3&am…...

文章解读与仿真程序复现思路——中国电机工程学报EI\CSCD\北大核心《考虑气电联合需求响应的气电综合能源配网系统协调优化运行》

这个标题涉及到一个涉及气体&#xff08;天然气&#xff09;和电力的综合能源配网系统&#xff0c;并且强调了考虑气电联合需求响应的协调优化运行。让我们逐步解读&#xff1a; 气电综合能源配网系统&#xff1a; 这指的是一个结合了气体&#xff08;通常是天然气&#xff09;…...

PostgreSQL 主键和唯一键的区别

主键和唯一键的区别 主键&#xff08;Primary Key&#xff09;&#xff1a; 主键是用于唯一标识表中的每一条记录的键。主键必须是唯一的&#xff0c;不允许为空。一个表只能有一个主键。主键可以由一个或多个字段组成。主键的值在整个表中必须是唯一的&#xff0c;用于确保数据…...

删除表格中的所有绘图

Ctrl G 调出定位的对话框再点击定位条件 按Delete键&#xff0c;删除...

Linux卸载Nginx

1、停止Nginx软件 #/usr/local/nginx/sbin/nginx-sstop 或者kill进程 #ps -ef|grep nginx #kill -9 PID 2、查找根下所有名子包含nginx的文件 #sudofind/-namenginx* 3、执行命令删掉nignx安装的相关文件 # rm -rf /usr/local/sbin/nginx # rm -rf /usr/local/nginx # r…...

Qt之QGraphicsView —— 笔记1:绘制简单图元(附完整源码)

效果 相关类介绍 QGraphicsView类提供了一个小部件,用于显示QGraphicsScene的内容。QGraphicsView在可滚动视口中可视化。QGraphicsView将滚动其视口,以确保该点在视图中居中。 QGraphicsScene类 提供了一个用于管理大量二维图形项的场景。请注意,QGraphicsScene没有自己的视…...

SpringIoC原理

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 本…...

如何对售后服务的全流程进行精细化的管理?

——“如何对售后服务的全流程进行精细化的管理&#xff1f;” ——“售后又是一个十分复杂的过程&#xff0c;仅靠手工或者电子表格记录这些内容&#xff0c;肯定是低效率、易出错的。最好的办法是借助合适的管理工具进行精细化的过程管理。” 假设你购买了一台新的家用电器…...

SAP UI5 walkthrough step2 Bootstrap

我的理解&#xff0c;这就是一个引导指令 1.我们右键打开命令行--执行 ui5 use OpenUI5 2.执行命令&#xff1a;ui5 add sap.ui.core sap.m themelib_sap_horizon 执行完之后&#xff0c;会更新 yaml 文件 3.修改index.html <!DOCTYPE html> <html> <head&…...

Gemini:定义下一代人工智能的里程碑

Google最近发布号称世界最强的大模型"Gemini"&#xff0c;其强大多模态LLM&#xff0c;标志着AI技术的一个新时代。 Gemini作为"迄今为止最强大的AI模型"之一&#xff0c;其独特之处在于它融合了多种模式的处理能力&#xff0c;能够同时理解和生成文本、代…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

云原生玩法三问:构建自定义开发环境

云原生玩法三问&#xff1a;构建自定义开发环境 引言 临时运维一个古董项目&#xff0c;无文档&#xff0c;无环境&#xff0c;无交接人&#xff0c;俗称三无。 运行设备的环境老&#xff0c;本地环境版本高&#xff0c;ssh不过去。正好最近对 腾讯出品的云原生 cnb 感兴趣&…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...