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 链表的定义和初始化 有两…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
comfyui 工作流中 图生视频 如何增加视频的长度到5秒
comfyUI 工作流怎么可以生成更长的视频。除了硬件显存要求之外还有别的方法吗? 在ComfyUI中实现图生视频并延长到5秒,需要结合多个扩展和技巧。以下是完整解决方案: 核心工作流配置(24fps下5秒120帧) #mermaid-svg-yP…...
