[SSM]GoF之代理模式
目录
十四、GoF之代理模式
14.1对代理模式的理解
14.2静态代理
14.3动态代理
14.3.1JDK动态代理
14.3.2CGLIB动态代理
十四、GoF之代理模式
14.1对代理模式的理解
-
场景:拍电影的时候,替身演员去代理演员完成表演。这就是一个代理模式。
-
演员为什么要找替身呢?(为什么要使用代理模式?)
-
怕自己受伤。(保护自己)
-
自己完成不了高难度动作。(功能增强)
-
-
在java程序中代理模式的作用:
-
当一个对象需要受到保护的时候,可以考虑使用代理对象去完成某个行为。
-
需要给某个对象的功能进行增强的时候,可以考虑找一个代理进行增强。
-
A对象无法和B对象直接交互,也可以使用代理模式来解决。
-
-
代理模式中有三个角色:
-
目标对象(演员)
-
代理对象(替身演员)
-
目标对象和代理对象的公共接口。(演员和替身演员应该具有相同的行为动作)
-
-
为什么演员和替身演员要有相同的行为动作呢?
-
不想让观众知道是替身演员,这里的观众其实就是“客户端程序”。
-
-
使用代理模式,对于客户端程序来说,客户端是无法察觉到的,客户端在使用代理对象的时候就像在使用目标对象。
-
代理模式是GoF23种设计模式之一,属于结构化设计模式。
-
代理模式在代码实现上,包括两种形式:
-
静态代理
-
动态代理
-
14.2静态代理
OrderService接口
package com.hhb.proxy.service;
//订单业务接口
public interface OrderService {//代理对象和目标对象的公共接口//生产订单void generate();
//修改订单信息void modify();
//查看订单详情void detail();
}
OrderServiceImpl
package com.hhb.proxy.service;
public class OrderServiceImpl implements OrderService {/*** 问题:统计所有业务接口的每一个业务方法的耗时。* 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序* 缺点:1.违背OCP开闭原则 2.代码没有得到复用* 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写* 缺点:1.虽然解决了OCP开闭原则,但是代码耦合度很高,因为采用了继承关系。* 2.代码没有得到复用* 解决方案三:代理模式* 优点:1.解决了OCP问题 2.采用代理模式的has a,可以降低耦合度** 目前使用的是静态代理,这个静态代理的缺点是:类爆炸* 解决方法:使用动态代理模式来解决这个问题。* 动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中为我们动态生成一个class字节码,这个字节码就是代理类* 在内存中动态生成字节码代理类的技术叫做:动态代理*/@Overridepublic void generate() {//目标方法//模拟生成订单的耗时try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已生成");}
@Overridepublic void modify() {//目标方法//模拟修改订单的耗时try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已修改");}
@Overridepublic void detail() {//目标方法//模拟查询订单的耗时try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("请看订单详情");}
}
OrderServiceProxy
package com.hhb.proxy.service;
//代理对象(代理对象和目标对象要具有相同的行为,就要实现同一个或同一些接口)
//客户端在使用代理对象的时候就像在使用目标对象一样。
public class OrderServiceProxy implements OrderService {//将目标对象作为代理对象的一个属性,这种关系叫做关联关系,比继承关系的耦合度低//代理对象中含有目标对象的引用。关联关系:has a//注意:这里要写一个公共接口类型,因为公共接口耦合度低private OrderService target;//这就是目标对象,目标对象一定实现了OrderService接口//创建代理对象的时候,传一个目标对象给代理对象。public OrderServiceProxy(OrderService target) {this.target = target;}
@Overridepublic void generate() {//代理方法//增强long begin = System.currentTimeMillis();//调用目标对象的目标方法target.generate();long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "");}
@Overridepublic void modify() {long begin = System.currentTimeMillis();target.modify();long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "");}
@Overridepublic void detail() {long begin = System.currentTimeMillis();target.detail();long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "");}
}
客户端
package com.hhb.proxy.client;
import com.hhb.proxy.service.OrderServiceImpl;
import com.hhb.proxy.service.OrderServiceProxy;
public class Test {public static void main(String[] args) {//创建目标对象OrderServiceImpl target = new OrderServiceImpl();//创建代理对象OrderServiceProxy proxy = new OrderServiceProxy(target);//调用代理对象的代理方法proxy.generate();proxy.modify();proxy.detail();}
}
-
以上就是代理模式中的静态代理,其中OrderService接口是代理类和目标类的共同接口。
-
OrderServiceImpl是目标类,OrderServiceProxy是代理类。
14.3动态代理
-
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量,解决代码复用的问题。
-
在内存当中动态生成类的技术常见的包括:
-
JDK动态代理技术:只能代理接口。
-
CGLIB动态代理技术:CGLIB是一个开源项目,是一个强大的、高性能、高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)
-
Javassist动态代理技术。
-
14.3.1JDK动态代理
OrderService接口
package com.hhb.proxy.service;
//订单业务接口
public interface OrderService {//代理对象和目标对象的公共接口//生产订单void generate();
//修改订单信息void modify();
//查看订单详情void detail();
}
OrderServiceImpl
package com.hhb.proxy.service;
public class OrderServiceImpl implements OrderService {@Overridepublic void generate() {//目标方法//模拟生成订单的耗时try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已生成");}
@Overridepublic void modify() {//目标方法//模拟修改订单的耗时try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已修改");}
@Overridepublic void detail() {//目标方法//模拟查询订单的耗时try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("请看订单详情");}
}
-
在动态代理中,OrderServiceProxy代理类是可以动态生成的,这个类不需要写,直接写客户端程序即可。
Client
package com.hhb.proxy.client;
import com.hhb.proxy.service.OrderService;
import com.hhb.proxy.service.OrderServiceImpl;
import com.hhb.proxy.service.TimerInvocationHandler;
import com.hhb.proxy.util.ProxyUtil;
import java.lang.reflect.Proxy;
public class Client {//客户端程序public static void main(String[] args) {//创建目标对象OrderService target = new OrderServiceImpl();//创建代理对象/* OrderService proxyObj = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new TimerInvocationHandler(target));*///使用工具类OrderService proxyObj = (OrderService) ProxyUtil.newProxyInstance(target);//调用代理对象的代理方法proxyObj.modify();proxyObj.detail();proxyObj.generate();String name = proxyObj.getName();System.out.println(name);}
}
-
创建代理对象
-
newProxyInstance翻译为:新建代理对象,也就是说,通过调用这个方法可以创建代理对象。本质上,这个Proxy.newProxyInstance()方法的执行做了两件事:
-
在内存中动态的生成了一个代理类的字节码class。
-
new对象了,通过内存中生成的代理类这个代码,实例化了代理对象。
-
-
关于newProxyInstance()方法的三个重要的参数:
-
第一个参数:ClassLoader loader
-
它是类加载器。在内存中生成的字节码也是class文件,要执行也得先加载到内存当中。加载类就需要类加载器,所以这里需要指定类加载器。并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。
-
-
第二个参数:Class<?>[] interfaces
-
代理类和目标类要实现同一个接口或同一些接口。
-
在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。
-
-
第三个参数:InvocationHandler h
-
InvocationHandler 被翻译为:调用处理器,是一个接口。
-
在调用处理器接口中编写的就是:增强代码。
-
既然是接口,就要写接口的实现类。
-
-
-
TimerInvocationHandler
package com.hhb.proxy.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class TimerInvocationHandler implements InvocationHandler {//目标对象private Object target;
public TimerInvocationHandler(Object target) {this.target = target;}
@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//这个接口的作用是写增强代码long begin = System.currentTimeMillis();//调用目标对象上的目标方法//方法四要素:哪个对象,哪个方法,传什么参数,返回什么值Object retValue = method.invoke(target, args);long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "毫秒");//注意:这个invoke方法的返回值,如果代理对象调用代理方法之后,需要返回结果的话,invoke方法必须将目标对象的目标方法执行结果继续返回return retValue;}
}
-
为什么要强行要求你必须实现InvocationHandler接口?
-
因为一个类实现接口就必须实现接口中的方法。
-
方法必须是invoke(),因为JDK在底层调用invoke()方法的程序已经提前写好了。
-
注意:invoke方法不是程序员调用的,是JDK负责调用。
-
-
invoke方法什么时候被调用?
-
当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。
-
-
invoke方法的三个参数
-
invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数,可以在invoke方法的大括号中直接使用。
-
第一个参数:Object proxy 代理对象的引用,这个参数使用较少。
-
第二个参数:Method method 目标对象上的目标方法。
-
第三个参数:Object[] args 目标方法上的实参。
-
invoke方法执行过程中,使用method来调用目标对象的目标方法。
-
工具类:ProxyUtil
package com.hhb.proxy.util;
import com.hhb.proxy.service.TimerInvocationHandler;
import java.lang.reflect.Proxy;
public class ProxyUtil {/*** 封装一个工具方法,可以通过这个方法获取代理对象** @param target* @return*/public static Object newProxyInstance(Object target) {return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new TimerInvocationHandler(target));}
}
14.3.2CGLIB动态代理
-
CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现,所以被代理的目标类不能使用final修饰。
引入依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
UserService
package com.hhb.proxy.service;
//目标类
public class UserService {//目标方法public boolean login(String username, String password) {System.out.println("系统正在验证身份");if ("admin".equals(username) && "123".equals(password)) {return true;}return false;}
//目标方法public void logout() {System.out.println("系统正在退出");}
}
-
使用CGLIB在内存中为UserService类生成代理类,并创建对象:
Client
package com.hhb.proxy.client;
import com.hhb.proxy.service.TimerMethodInterceptor;
import com.hhb.proxy.service.UserService;
import net.sf.cglib.proxy.Enhancer;
public class Client {public static void main(String[] args) {//创建字节码增强器对象//这个对象是CGLIB库当中的核心对象,就是依靠它来生成代理类Enhancer enhancer = new Enhancer();
//告诉CGLIB父类是谁,告诉CGLIB目标类是谁enhancer.setSuperclass(UserService.class);
//设置回调(等同于JDK动态代理当中的调用处理器。InvocationHandler)//在CGLIB当中不是InvocatioHandler接口,是方法拦截器:MethodInterceptorenhancer.setCallback(new TimerMethodInterceptor());
//创建代理对象//1.在内存中生成UserService类的子类,其实就是代理类的字节码。//2.创建代理对象UserService userServiceProxy = (UserService) enhancer.create();
//调用代理对象的代理方法boolean succes = userServiceProxy.login("admin", "123");System.out.println(succes ? "登录成功" : "登录失败");
userServiceProxy.logout();}
}
编写MethodInterceptor接口实现类
package com.hhb.proxy.service;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TimerMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {//前面增强long begin = System.currentTimeMillis();
//调用目标对象的目标方法Object retValue = methodProxy.invokeSuper(target, objects);
//后面增强long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "毫秒");
return retValue;}
}
相关文章:

[SSM]GoF之代理模式
目录 十四、GoF之代理模式 14.1对代理模式的理解 14.2静态代理 14.3动态代理 14.3.1JDK动态代理 14.3.2CGLIB动态代理 十四、GoF之代理模式 14.1对代理模式的理解 场景:拍电影的时候,替身演员去代理演员完成表演。这就是一个代理模式。 演员为什…...

桥梁安全生命周期监测解决方案
一、方案背景 建筑安全是人们生产、经营、居住等经济生活和人身安全的基本保证,目前我国越来越多的建筑物逐 步接近或者已经达到了使用年限,使得建筑物不断出现各种安全隐患,对居民的人身安全和财产安全产 生不利影响,因此房…...

图技术在 LLM 下的应用:知识图谱驱动的大语言模型 Llama Index
LLM 如火如荼地发展了大半年,各类大模型和相关框架也逐步成型,可被大家应用到业务实际中。在这个过程中,我们可能会遇到一类问题是:现有的哪些数据,如何更好地与 LLM 对接上。像是大家都在用的知识图谱,现在…...

SpringBoot自动配置、启动器原理爆肝解析(干货满满)
文章目录 前言一、SpringBoot优势概要二、SpringBoot自动配置1. ☠注意☠2.自动配置详解 三、Starter(场景启动器)原理总结 前言 本文详细解析面试重点—SpringBoot自动配置原理、场景启动器原理,深入源码,直接上干货、绝不拖泥带…...

chrome扩展控制popup页面动态切换
文章目录 1、通过控制元素的显示隐藏达到popup页面切换的效果2、通过监听页面重新加载完成不同popup的切换3、直接修改popup页面location.href,无需刷新页面 1、通过控制元素的显示隐藏达到popup页面切换的效果 下面在mv2版本的API下完成 实际上通过控制页面元素实…...

【AI】《动手学-深度学习-PyTorch版》笔记(三):PyTorch常用函数
AI学习目录汇总 1、torch.arange 返回一维张量(一维数组),官网说明,常见的三种用法如下 输入:torch.arange(5) 输出:tensor([0, 1, 2, 3, 4]) 输入:torch.arange(5, 16) 输出:tensor([ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) 输入:torch.arange(1, 25, 2) …...

某文化馆三维建模模型-glb格式-三维漫游-室内导航测试
资源描述 某文化馆某个楼层的三维建模模型,glb格式,适用于three.js开发,可用来做一些三维室内漫游测试和室内导航测试 资源下载地址...

网络安全 Day19-计算机网络基础知识04(网络协议)
计算机网络基础知识04(网络协议) 1. ARP1.1 ARP通讯原理1.2 arp欺骗1.3 ARP欺骗与预防1.4 排查ARP病毒 2. DHCP工作原理(自动分配内网IP)3. TCP协议三次握手、四次挥手原理4. DNS协议工作原理 1. ARP Linux查看arp:ar…...

Verilog语法学习——LV5_位拆分与运算
LV5_位拆分与运算 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述: 现在输入了一个压缩的16位数据,其实际上包含了四个数据…...

❤️创意网页:创意动态画布~缤纷移动涂鸦~图片彩色打码
✨博主:命运之光 🌸专栏:Python星辰秘典 🐳专栏:web开发(简单好用又好看) ❤️专栏:Java经典程序设计 ☀️博主的其他文章:点击进入博主的主页 前言:欢迎踏入…...

数值分析第六章节 用Python实现解线性方程组的迭代法
参考书籍:数值分析 第五版 李庆杨 王能超 易大义编 第5章 解线性方程组的迭代法 文章声明:如有发现错误,欢迎批评指正 文章目录 迭代法的基本概念雅可比迭代法与高斯-塞格尔迭代法雅可比迭代法高斯-塞格尔迭代法 迭代法的基本概念 6.1.1引言…...

【低代码专题方案】使用iPaaS平台下发数据,快捷集成MDM类型系统
01 场景背景 伴随着企业信息化建设日趋完善化、体系化,使用的应用系统越来越多,业务发展中沉淀了大量数据。主数据作为数据治理中枢,保存大量标准数据库,如何把庞大的数据下发到各个业务系统成了很棘手的问题。 传统的数据下发方…...

驱动开发 day3 (模块化驱动启动led,蜂鸣器,风扇,震动马达)
模块化驱动启动led,蜂鸣器,风扇,震动马达并加上Makefile 封装模块化驱动,可自由安装卸载驱动,便于驱动更新(附图) 1.安装模块驱动同时初始化各个设备并使能 2.该驱动会自动创建驱动节点. 3.通过c函数程序输入控制各个设备 4.卸载模块驱动 //编译驱动…...

数据结构与算法基础-学习-27-图之最短路径之Dijkstra(迪杰斯特拉)算法
一、最短路径应用案例 例如从北京到上海旅游,有多条路可以到目的地,哪条路线最短,哪条路线最省钱,就是典型的最短路径问题。 二、最短路径问题分类 最短路径问题可以分为两类,第一类为:两点间最短路径。第…...

Windows Server 2012 能使用的playwright版本
由于在harkua_bot里面使用到了playwright,我的服务器又是Windows Server 2012 R2,最新版playwright不支持Windows Server 2012 R2,支持Windows Server 2016以上,所以有了这个需求 https://cdn.npmmirror.com/binaries/playwright…...

css实现溢出变为省略号
单行文本溢出省略 text-overflow:规定当文本溢出时,显示省略符号来代表被修剪的文本 white-space:设置文字在一行显示,不能换行 overflow:文字长度超出限定宽度,则隐藏超出的内容overflow设为hidden&#…...

nginx如何配置两个服务器的连接
nginx 中通过server_name listen的方式配置多个服务器 nginx配置两个站点的windows操作方法,双域名双站点...

Linux环境Arduino IDE中配置ATOM S3
linux选择ubuntu发行版。 硬件设备有多小呢: 功能超级强大。 之前的ROS1和ROS2案例已经全部移植完成并测试结束(三轮纯人力校验😎)。 官网文档信息非常非常好: https://docs.m5stack.com/zh_CN/quick_start/atoms3…...

【C#】.Net Framework框架下的Authorize权限类
2023年,第31周,第3篇文章。给自己一个目标,然后坚持总会有收货,不信你试试! 在C#的.NET Framework中,你可以使用Authorize类来处理权限认证。Authorize类位于System.Web.Mvc命名空间中,它提供了…...

C++ list底层实现原理
文章目录 一、list底层实现二、类构成三、构造函数四、迭代器五、获取第一个元素六、获取最后一个元素七、插入元素 一句话:list底层实现一个双向循环链表 一、list底层实现 一个双向循环链表 二、类构成 class list : protected_List_base_list_base.lsit_impl…...

C#实现数字验证码
开发环境:VS2019,.NET Core 3.1,ASP.NET Core API 1、建立一个验证码控制器 新建两个方法Create和Check,Create用于创建验证码,Check用于验证它是否有效。 声明一个静态类变量存放列表,列表中存放包含令…...

Git的常用命令以及使用场景
文章目录 1.前言2.工作区,暂存区,版本库简介3.Git的常用命令4.版本回退5.撤销修改6.删除文件7.总结 1.前言 在学习Git命令之前,需要先了解工作区,暂存区和版本库这三个概念 2.工作区,暂存区,版本库简介 在使用Git进行版本控制时,有三个重要的概念:工作…...

tcp keepalive
tcp keepalive用于检查两者之间的链路是否正常,或防止链路断开。 一旦建立了TCP连接,该连接被定义为有效,直到一方关闭它。一旦连接进入连接状态,它将无限期地保持连接状态。但实际上,这种联系不会无限期地持续下去。如…...

PP-Matting: AI高精度图像前景Matting,让抠图轻而易举
分割和Matting的一个重要区别是:分割返回的是像素分类标签,其结果是整型数据;而Matting返回的是属于前景或背景的概率P,从而在前景与背景交互区域产生渐变的效果,使得抠图更加自然。Matting分割模型训练完成后,对于原始图像每个位置上的像素,都将生成一个表示其前景透明…...

VUE3-01
1.选项式和组合式 选项式API:按照作用组织代码 组合式API:按照功能组织代码 2.<script setup> <template><div class"about"><h1>{{name}}</h1><button click"sayHello">测试</button>…...

分库分表之基于Shardingjdbc+docker+mysql主从架构实现读写分离(二)
说明:如果实现了docker部署mysql并完成主从复制的话再继续,本篇文章主要说明springboot配置实现Shardingjdbc进行读写分离操作。 如果没实现docker部署mysql实现主从架构的话点击我 Shardingjdbc配置介绍(版本:5.3.2)…...

Python 进阶(四):日期和时间(time、datetime、calendar 模块)
❤️ 博客主页:水滴技术 🌸 订阅专栏:Python 入门核心技术 🚀 支持水滴:点赞👍 收藏⭐ 留言💬 文章目录 1. time模块1.1 获取当前时间1.2 时间休眠1.3 格式化时间 2. datetime模块2.1 获取当前…...

Transformer背景介绍
目录 Transformer的诞生Transformer的优势Transformer的市场 Transformer的诞生 论文地址 Transformer的优势 Transformer的市场...

深入理解BeanDefinition和Spring Beans
深入理解BeanDefinition和Spring Beans 引言 在Spring框架中,BeanDefinition和Spring Beans是非常重要的概念。BeanDefinition定义了Spring Bean的元数据,而Spring Beans是应用程序中的对象实例。理解BeanDefinition和Spring Beans的概念和使用方法对于…...

实验六 调度器-实验部分
目录 一、知识点 1.进程调度器设计的目标 1.1.进程的生命周期 1.2.用户进程创建与内核进程创建 1.3.进程调度器的设计目标 2.ucore 调度器框架 2.1.调度初始化 2.2.调度过程 2.2.1.调度整体流程 2.2.2.设计考虑要点 2.2.3.数据结构 2.2.4.调度框架应与调度算法无关…...