Spring学习笔记11 GoF代理模式
Spring学习笔记10 JdbcTemplate_biubiubiu0706的博客-CSDN博客
新建个maven模块 static-proxy
演示静态代理
订单接口


测试

需求:统计每个业务方法的耗时
package com.example.proxy.service;/*** @author hrui* @date 2023/9/25 8:42*/
public class OrderServiceImpl implements OrderService{@Overridepublic void generate() {long start = System.currentTimeMillis();//模拟生成订单耗时try {Thread.sleep(1234);System.out.println("订单已生产");} catch (InterruptedException e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");}@Overridepublic void modify() {long start = System.currentTimeMillis();try {Thread.sleep(2222);System.out.println("订单已修改");} catch (InterruptedException e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");}@Overridepublic void detail() {long start = System.currentTimeMillis();try {Thread.sleep(3333);System.out.println("查看订单详情");} catch (InterruptedException e) {e.printStackTrace();}long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");}
}

上面确实实现了功能
但是硬编码,在每个业务接口中的每一个业务方法中直接添加统计耗时的程序:
缺点:
1.违背了OCP开闭原则
2. 代码没有得到复用
--------------------------------------------------------------------------------------------------------------------
package com.example.proxy.service;/*** @author hrui* @date 2023/9/25 8:42*/
public class OrderServiceImpl implements OrderService{@Overridepublic void generate() {//模拟生成订单耗时try {Thread.sleep(1234);System.out.println("订单已生产");} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic void modify() {try {Thread.sleep(2222);System.out.println("订单已修改");} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic void detail() {try {Thread.sleep(3333);System.out.println("查看订单详情");} catch (InterruptedException e) {e.printStackTrace();}}
}
让子类继承重写方法
package com.example.proxy.service;/*** @author hrui* @date 2023/9/25 9:48*/
public class OrderServiceImplSub extends OrderServiceImpl{@Overridepublic void generate() {long start = System.currentTimeMillis();super.generate();long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");}@Overridepublic void modify() {long start = System.currentTimeMillis();super.modify();long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");}@Overridepublic void detail() {long start = System.currentTimeMillis();super.detail();long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");}
}
测试

上面的解决方式解决了OCP
但是这种方式会导致耦合度很高.因为采用了继承关系.继承关系是一种耦合度非常高的关系,不建议使用.
另外代码还是没有得到复用
再看下面的解决方式(静态代理)
OrderService是公共接口 OrderServiceImpl是目标对象
package com.example.proxy.service;import com.sun.org.apache.xpath.internal.operations.Or;/*** @author hrui* @date 2023/9/25 10:24*/
public class OrderServiceProxy implements OrderService{//和目标对象应有相同的行为动作,因此实现OrderServiceOrderService target;public OrderServiceProxy(OrderService target) {this.target = target;}@Overridepublic void generate() {//代理方法long start = System.currentTimeMillis();target.generate();long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");}@Overridepublic void modify() {long start = System.currentTimeMillis();target.modify();long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");}@Overridepublic void detail() {long start = System.currentTimeMillis();target.detail();long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");}
}

上面使用了静态代理,目标对象和代理对象都需要实现或者继承公共接口,因为需要相同的动作或行为.
解决了OCP原则问题,使用静态代理可以降低耦合度
缺点:如果程序中有多个这样业务类,那么都需要代理,类爆炸 不好维护
可以使用动态代理解决类爆炸问题
动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中动态的生成.class字节码,这个字节码就是代理类----->在内存中动态的生成字节码技术
SpringAOP底层用的不是JDK就是CGLIB
动态代理:
1.JDK代理:目标类必须实现接口(缺陷)
2.CGLIB代理:无要求,且性能比JDK代理好.底层是通过继承实现的(因动态在内存中生成,无所谓耦合度了)----->底层还有一个小而快的字节码处理框架ASM
3.javassist:动态代理
java的反射包下有个Proxy java.lang.reflect.Proxy(JDK内置的)--->帮我们在内存中动态生成字节码
新建模块 dynamic-proxy


还是同一个需求,统计每个业务方法的耗时
直接开始写

具体代码
package com.example.proxy.client;import com.example.proxy.service.OrderService;
import com.example.proxy.service.OrderServiceImpl;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @author hrui* @date 2023/9/25 12:41*/
public class Client {public static void main(String[] args) {//创建目标对象OrderService target=new OrderServiceImpl();//创建代理对象/*** 1.newProxyInstance 翻译为:新建代理对象* 也就是说,通过调用这个方法可以创建代理对象* 本质上newProxyInstance()方法的执行,做了两件事:* 第一件事:在内存中动态的生成了一个代理类的字节码class* 第二件事:new对象了.通过内存中生成的代理类.class,实例化了代理对象* 2.关于newProxyInstance()方法的三个重要的参数,每一个含义,有什么用?* 第一个参数:ClassLoader loader* 类加载器 必须得和目标类得类加载器一样* 第二个参数:Class<?>[] interfaces* 代理类和目标类要实现同一个接口或同一些接口.* 第三个参数:InvocationHandler h* InvocationHandler是一个接口,称为调用处理器,需要传一个InvocationHandler实现类* 调用处理器不会导致类爆炸,这里就不做处理,以匿名内类方式*///Object proxyObj = Proxy.newProxyInstance("类加载器", "代理类要实现的接口", "调用处理器");OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {/*1.invoke方法是JDK负责调用的2.当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()会被调用3.invoke方法内的三个参数:invoke()是JDK负责调用的.参数是JDK传过来的.第一个参数:Object proxy 代理对象的引用,这个参数使用较少第二个参数:Method method 目标对象上的目标方法.(要执行的目标方法就是它)第三个参数:Object[] args 目标方法上的实参*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {long start = System.currentTimeMillis();Object retValue = method.invoke(target, args);long end = System.currentTimeMillis();System.out.println("耗时"+(end-start)+"毫秒");return null;}});//调用代理对象的代理方法//注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行(在执行前后添加功能增强功能)proxyObj.generate();proxyObj.modify();proxyObj.detail();}
}
上面是匿名内部类写法
而且关于返回值 返回了是null

返回null,但程序依旧是可以跑的 但上面所有的方法是无返回值的,如果是有返回值的会如何
新增一个有返回值的


程序还是可以跑

但返回值能不能拿到


下面是用个实现类的写法
package com.example.proxy.client;import com.example.proxy.service.OrderService;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** @author hrui* @date 2023/9/25 15:57*/
public class AInvocationHandler implements InvocationHandler {OrderService target;public AInvocationHandler(OrderService target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object invoke = method.invoke(target, method);return invoke;}
}
JDK动态代理工具类封装
public class JDKProxyUtil {public static Object newProxyInstance(Object target){//底层还是调用JDK的动态代理return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object retValue = method.invoke(target, args);return retValue;}});}
}
package com.example.proxy.client;import com.example.proxy.service.OrderService;
import com.example.proxy.service.OrderServiceImpl;
import com.example.proxy.util.JDKProxyUtil;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @author hrui* @date 2023/9/25 12:41*/
public class Client {public static void main(String[] args) {//创建目标对象OrderService target=new OrderServiceImpl();//创建代理对象/*** 1.newProxyInstance 翻译为:新建代理对象* 也就是说,通过调用这个方法可以创建代理对象* 本质上newProxyInstance()方法的执行,做了两件事:* 第一件事:在内存中动态的生成了一个代理类的字节码class* 第二件事:new对象了.通过内存中生成的代理类.class,实例化了代理对象* 2.关于newProxyInstance()方法的三个重要的参数,每一个含义,有什么用?* 第一个参数:ClassLoader loader* 类加载器 必须得和目标类得类加载器一样* 第二个参数:Class<?>[] interfaces* 代理类和目标类要实现同一个接口或同一些接口.* 第三个参数:InvocationHandler h* InvocationHandler是一个接口,称为调用处理器,需要传一个InvocationHandler实现类* 调用处理器不会导致类爆炸,这里就不做处理,以匿名内类方式*///Object proxyObj = Proxy.newProxyInstance("类加载器", "代理类要实现的接口", "调用处理器");
// OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
//
//
//
// /*
// 1.invoke方法是JDK负责调用的
// 2.当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()会被调用
// 3.invoke方法内的三个参数:
// invoke()是JDK负责调用的.参数是JDK传过来的.
// 第一个参数:Object proxy 代理对象的引用,这个参数使用较少
// 第二个参数:Method method 目标对象上的目标方法.(要执行的目标方法就是它)
// 第三个参数:Object[] args 目标方法上的实参
//
// */
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// long start = System.currentTimeMillis();
// Object retValue = method.invoke(target, args);
// long end = System.currentTimeMillis();
// System.out.println("耗时"+(end-start)+"毫秒");
// //如果代理对象调用代理方法之后,需要返回结果的话,invoke方法必须将目标对象的目标方法的返回结果继续返回
// return retValue;
// }
// });OrderService proxyObj = (OrderService)JDKProxyUtil.newProxyInstance(target);//调用代理对象的代理方法//注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行(在执行前后添加功能增强功能)proxyObj.generate();proxyObj.modify();proxyObj.detail();String name = proxyObj.getName();System.out.println(name);}
}
CGLIB既可以代理接口,又可以代理类.底层采用继承的方式实现,所以被代理的目标类不能使用final修饰
使用CGLIB需要引入依赖
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version> </dependency>
其实CGLIB和开头写的继承方式差不多,只不过一个是需要写的而CGLIB是制动内存中生成的


相关文章:
Spring学习笔记11 GoF代理模式
Spring学习笔记10 JdbcTemplate_biubiubiu0706的博客-CSDN博客 新建个maven模块 static-proxy 演示静态代理 订单接口 测试 需求:统计每个业务方法的耗时 package com.example.proxy.service;/*** author hrui* date 2023/9/25 8:42*/ public class OrderServiceImpl implem…...
代码随想录二刷 Day23
669. 修剪二叉搜索树 找到小数字的右子树与大数字左子树必须要重新检查一遍然后让root的左右直接指向return的左右节点; class Solution { public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (root NULL) return NULL;if (root->val < low…...
Ubuntu `apt` 报错 “Errors were encountered while processing: base-passwd“ 的解决方法
Ubuntu apt 更新时出现报错: Setting up base-passwd (3.5.52build1) ... Changing home-directory of irc from /var/run/ircd to /run/ircd 1 changes have been made, rewriting files Writing passwd-file to /etc/passwd Error making backupfile /etc/passwd…...
XXL-JOB分布式任务调度
XXL-JOB分布式任务调度 在实际项目中,为了降低耦合,通常会把定时任务的逻辑单独抽离出来,构建成一个新的工程。也有可能需要定时任务实现高可用,组建成集群,提高容错率。 那么问题也就来了。既然定时任务是多个…...
加拿大人工智能数据搜索平台【Secoda】完成1400万美元A轮融资
来源:猛兽财经 作者:猛兽财经 猛兽财经获悉,总部位于加拿大多伦多的人工智能数据搜索平台【Secoda】今日宣布已完成1400万美元A轮融资。 本轮融资由Craft Ventures领投,参与投资的投资机构有Abstract Ventures、现有投资者YCombi…...
less与sass
1.变量: Less: my-color: #ff0000;.container {background-color: my-color; } Sass:$my-color: #ff0000;.container {background-color: $my-color; } 在这点上,Less和Sass的变量概念基本相同,都是以声明的方式存储值,然后在…...
已解决: Go Error: no Go files in /path/to/directory问题
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页: 🐅🐾猫头虎的博客🎐《面试题大全专栏》 🦕 文章图文并茂🦖…...
2022年6月和7月的工作经历
6月 3D打标软件 3D打标软件,要求在Open3d上加几个2D文字。大致有如下几个方案: 依葫芦画瓢,但O3DVisualizer派生于gui::Window,我的程序派生于Visualizer。工作量不小。 利用OpenGL输出文字,Baidu的两种方法一个编…...
【图像处理】SIFT角点特征提取原理
一、说明 提起在OpenCV中的特征点提取,可以列出Harris,可以使用SIFT算法或SURF算法来检测图像中的角特征点。本篇围绕sift的特征点提取,只是管中窥豹,而更多的特征点算法有: Harris & Stephens / Shi–Tomasi 角点…...
flutter开发实战-应用更新apk下载、安装apk、启动应用实现
flutter开发实战-应用更新apk下载、安装apk、启动应用实现 在开发过程中,经常遇到需要更新下载新版本的apk文件,之后进行应用更新apk下载、安装apk、启动应用。我们在flutter工程中实现下载apk,判断当前版本与需要更新安装的版本进行比对判断…...
DispatcherServlet初始化之Spring容器创建1.0
一、前言 在SpringMVC框架中,DispatcherServlet扮演着非常重要的角色,它负责接收所有的HTTP请求并将其分发给相应的处理器。在DispatcherServlet的初始化过程中,会创建一个Spring容器来管理应用程序中的Bean。 二、步骤 1、加载配置文件&a…...
CSS的基础
CSS美化HTML,布局网页 CSS最大的价值:由HTML专注去做结构呈现,样式给CSS,结构(HTML)与样式(CSS)相分离 CSS主要由选择器以及一条或多条声明 在<head></head>中实现CSS在<body…...
mathtype如何嵌入到word中?详细mathtype安装步骤教程
mathtype是一款功能特别强大的数学方式编辑软件,为用户提供各种强大的数学公式符号帮助用户进行计算,并且速度很快。有小伙伴知道mathtype如何嵌入到word中吗,这里小编就给大家详细介绍一下mathtype嵌入到word中的方法,有需要的小…...
云安全之访问控制的常见攻击及防御
访问控制攻击概述 访问控制漏洞即应用程序允许攻击者执行或者访问某种攻击者不具备相应权限的功能或资源。 常见的访问控制可以分为垂直访问控制、水平访问控制及多阶段访问控制 (上下文相关访问控制),与其相应的访问控制漏洞为也垂直越权漏洞(普通用户可以访问或…...
Java编程技巧:跨域
目录 1、跨域概念2、后端CORS(跨域资源共享)配置原理3、既然请求跨域了,那么请求到底发出去没有?4、通过后端CORS(跨域资源共享)配置解决跨域问题代码4.1、SpringBoot(FilterRegistrationBean&a…...
react create-react-app 配置less
环境信息: create-react-app:v5 react:18.2.0 node:18.16.0 如果你不必须使用 less 建议直接使用scss。 因为less配置会遇到很多问题。 配置less过程: 如果你只需要 sass的话,就可以直接使用sass。因为默认配置了scss。 npm、yarn、cnpm、…...
树的表示——孩子兄弟表示法
从图中可以看出,树的每个结点,都有不确定的指向他们的孩子的节点,如果我们定义这样一个结构体来便是数的结构的话: struct TreeNode { int val; struct TreeNodep1; struct TreeNodep1; … }; 是不能够表示一棵树的,因…...
Windows11安装MySQL8.1
安装过程中遇到任何问题均可以参考(这个博客只是单纯升级个版本和简化流程) Windows安装MySQL8教程-CSDN博客 到官网下载mysql8数据库软件 MySQL :: Download MySQL Community Server 下载完后,解压到你需要安装的文件夹 其中的配置文件内容了如下 [mysqld]# 设置3306端口po…...
Linux编程——经典链表list_head
1. 关于list_head struct list_head是Linux内核定义的双向链表,包含一个指向前驱节点和后继节点的指针的结构体。其定义如下: struct list_head {struct list_head *next, *prev; //双向链表,指向节点的指针 };1.1 链表的定义和初始化 有两…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
