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

Spring AOP 原理——代理模式

目录

一、代理模式

1.1  静态代理

1.2 动态代理

1.2.1 JDK动态代理

1.2.2 CGLIB动态代理


Spring AOP 是基于动态代理来实现AOP的。

一、代理模式

代理模式, 也叫委托模式。该模式是为其他对象提供⼀种代理以控制对这个对象的访问。它的作用就是通过提供一个代理类,让我们在调用目标方法的时候,不再是直接对目标方法进行调用,而是通过代理类间接调用。

在某些情况下, 一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

使用代理前:

使用代理后:

代理模式的主要角色:

  1. Subject: 业务接口类。可以是抽象类或者接口(不一定有)。
  2. RealSubject: 业务实现类。具体的业务执行, 也就是被代理对象。
  3. Proxy: 代理类。RealSubject的代理。

根据代理的创建时期,代理模式分为静态代理和动态代理:

  • 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
  • 动态代理:在程序运行时,运用反射机制动态创建而成。

1.1  静态代理

静态代理:在程序运行前, 代理类的 .class文件就已经存在了。

举例:房租租赁

//1.定义接口(中介需要做的事情)
public interface HouseSubject {void rentHouse();
}//2. 实现接⼝(房东出租房⼦)
public class RealHouseSubject implements HouseSubject{@Overridepublic void rentHouse() {System.out.println("我是房东, 我出租房⼦");}
}//3.代理(中介, 帮房东出租房⼦)
public class HouseProxy implements HouseSubject{//将被代理对象声明为成员变量private HouseSubject houseSubject;public HouseProxy(HouseSubject houseSubject) {this.houseSubject = houseSubject;}@Overridepublic void rentHouse() {//开始代理System.out.println("我是中介, 开始代理");//代理房东出租房⼦houseSubject.rentHouse();//代理结束System.out.println("我是中介, 代理结束");}
}//4.使用
public class StaticMain {public static void main(String[] args) {HouseSubject subject = new RealHouseSubject();//创建代理类HouseProxy proxy = new HouseProxy(subject);//通过代理类访问⽬标⽅法proxy.rentHouse();}
}

这种方式将代码写死,非常不灵活,如果后续需要增加业务,只能一个个增加修改。 

1.2 动态代理

相比于静态代理来说,动态代理更加灵活。该代理不需要针对每个目标对象都单独创建一个代理对象,而是把这个创建代理对象的工作推迟到程序运行时由JVM来实现。也就是说动态代理在程序运行时,根据需要动态创建生成。
Java也对动态代理进行了实现,并给我们提供了一些API,常见的实现方式有两种:
  1. JDK动态代理
  2. CGLIB动态代理

1.2.1 JDK动态代理

JDK 动态代理类实现步骤:
  1. 定义一个接口及其实现类(静态代理中的 HouseSubject RealHouseSubject );
  2. 自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中我们会调用目标方法(被代理类的方法)并自定义一些处理逻辑;
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[ ] interfaces,InvocationHandler h) 方法创建代理对象。

简单实现:

//1.实现 InvocationHandler 接口
mport java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
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 {// 代理增强内容System.out.println("我是中介, 开始代理");//通过反射调⽤被代理类的⽅法Object retVal = method.invoke(target, args);//代理增强内容System.out.println("我是中介, 代理结束");return retVal;}
}//2.创建⼀个代理对象并使用
public class DynamicMain {public static void main(String[] args) {HouseSubject target= new RealHouseSubject();//创建⼀个代理类:通过被代理类、被代理实现的接⼝、⽅法调⽤处理器来创建HouseSubject proxy = (HouseSubject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{HouseSubject.class},new JDKInvocationHandler(target));proxy.rentHouse();}
}

 其中:

  • InvocationHandler接口是Java动态代理的关键接口之一,它定义了一个单一方法 invoke() ,用于处理被代理对象的方法调用。
public interface InvocationHandler {/*** 参数说明* proxy:代理对象* method:代理对象需要实现的⽅法,即其中需要重写的⽅法* args:method所对应⽅法的参数*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}

通过实现 InvocationHandler 接口,可以对被代理对象的方法进行功能增强。

  •  Proxy 类中使用频率最高的方法是: newProxyInstance() , 这个方法主要用来生成一个代理对象。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{//...代码省略}
这个方法一共有 3 个参数:
  • Loader:类加载器, 用于加载代理对象;
  • interfaces:被代理类实现的一些接口(这个参数的定义,也决定了JDK动态代理只能代理实现了接口的一些类);
  • h :实现了 InvocationHandler 接口的对象。

1.2.2 CGLIB动态代理

JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。有些场景下, 业务代码是直接实现的,并没有接口定义。此时 CGLIB 动态代理机制可以解决这个问题。
CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理,很多知名的开源框架都使用到了CGLIB。例如 Spring 中的 AOP 模块中:如果目标对象实现了接口,则默认采用 JDK 动态代理, 否则采用 CGLIB 动态代理。
CGLIB 动态代理类实现步骤:
  1. 定义⼀个类(被代理类);
  2. 定义 MethodInterceptor 并重写 intercept 方法, intercept 用于增强目标方法,和 JDK 动态代理中的 invoke 方法类似;
  3. 通过 Enhancer 类的 create( )创建代理类。

简单实现:

1.添加依赖

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

2. 自定义MethodInterceptor(方法拦截器)

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;//实现MethodInterceptor接口
public class CGLIBInterceptor implements MethodInterceptor {//⽬标对象, 即被代理对象private Object target;public CGLIBInterceptor(Object target){this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] objects, 
MethodProxy methodProxy) throws Throwable {// 代理增强内容System.out.println("我是中介, 开始代理");//通过反射调⽤被代理类的⽅法Object retVal = methodProxy.invoke(target, objects);//代理增强内容System.out.println("我是中介, 代理结束");return retVal;}
}

3.创建代理类, 并使用

public class DynamicMain {public static void main(String[] args) {HouseSubject target= new RealHouseSubject();HouseSubject proxy= (HouseSubject) 
Enhancer.create(target.getClass(),new CGLIBInterceptor(target));proxy.rentHouse();}
}

其中:

  •  MethodInterceptor 和 JDK动态代理中的 InvocationHandler 类似,它只定义了一个方intercept(),用于增强目标方法。
 /*** 参数说明:* o: 被代理的对象* method: ⽬标⽅法(被拦截的⽅法, 也就是需要增强的⽅法)* objects: ⽅法⼊参* methodProxy: ⽤于调⽤原始⽅法*/Object intercept(Object o, Method method, Object[] objects, MethodProxy 
methodProxy) throws Throwable;
}
  • Enhancer.create() 用来生成一个代理对象
public static Object create(Class type, Callback callback) {//...代码省略
}
这个方法中的参数说明:
  • type:被代理类的类型(类或接口);
  • callback:自定义方法拦截器 MethodInterceptor。

相关文章:

Spring AOP 原理——代理模式

目录 一、代理模式 1.1 静态代理 1.2 动态代理 1.2.1 JDK动态代理 1.2.2 CGLIB动态代理 Spring AOP 是基于动态代理来实现AOP的。 一、代理模式 代理模式, 也叫委托模式。该模式是为其他对象提供⼀种代理以控制对这个对象的访问。它的作用就是通过提供一个代理类&#…...

leetcode 234.回文链表

思路&#xff1a;其实就是判断反转链表是不是和原链表一样的问题。 我们可以借助反转链表的思路&#xff0c;首先我们先把链表的全部元素正向存储&#xff0c;然后再把链表进行反转。 之后我们再遍历反转之后的链表结点元素是不是和刚刚存储数组里面的元素一致就可以了。一旦…...

AD中Split Planes 的作用和功能

在 Altium Designer (AD) 中&#xff0c;Split Planes 功能允许你在一个平面层&#xff08;例如电源层或地层&#xff09;上分割出多个不同的区域&#xff0c;每个区域可以分配给不同的网络&#xff08;net&#xff09;。这对于设计中需要管理多种电源或接地类型的情况下非常有…...

[linux][命令]linux文件操作命令大全

Linux操作系统提供了丰富的文件操作命令&#xff0c;以下是一些常用的文件操作命令列表&#xff1a; 查看文件内容 cat&#xff1a;查看文件内容。less&#xff1a;分页显示文件内容。more&#xff1a;分页显示文件内容&#xff0c;一次显示一屏。head&#xff1a;查看文件的前…...

大语言模型 (LLM) 窥探未来

初始的探索 在NLP领域&#xff0c;早期的模型如 LSTM 和 GRU 在处理序列数据时取得了一定的成功。但随着数据量和复杂性的增加&#xff0c;这些模型开始显得力不从心。 Transformer的诞生 Transformer 模型的提出&#xff0c;它通过自注意力&#xff08;Self-Attention&…...

WPF DataGrid调试错误总结

最近WPF中使用了DataGrid做了表格&#xff0c;框架版本为472&#xff0c;遇到了不少的问题&#xff0c;因为软件添加了一个退出进程的全局错误捕获&#xff0c;因此不得不解决所有问题&#xff0c;这边总结一下DataGrid的问题 EditItem is not allowed for this view 按字面意…...

【GCC】结合GPT4 延迟梯度学习1:公式推导及理论分析

大神的分析 本文主要借鉴。【TWCC 】基于gpt和python简化分析webrtc拥塞控制论文: Analysis and Design of the Google Congestion Contro for Web Real-time Communication (WebRTC)感觉应该学习好理论后再进行python 分析:【gcc】基于gpt和python的流程和延迟梯度分析另外:…...

【Linux】【网络】进程间关系与守护进程

进程间关系与守护进程 文章目录 1.进程组1.1什么是进程组1.2组长进程 2.会话2.1什么是会话2.2如何创建会话 3.作业3.1什么是作业、作业控制&#xff1f;3.2作业号3.3常见作业状态3.4作业的切换 4.守护进程4.1什么是守护进程&#xff1f;4.2如何创建守护进程4.3模拟实现daemon …...

红黑树的插入与删除

文章目录 红黑树概念红黑树的性质&#xff1a; 红黑树的插入操作情况一情况二情况三 小总结红黑树的验证红黑树的删除一.删除单孩子节点1. 删除节点颜色为黑色2. 删除颜色为红色 二. 删除叶子节点1. 删除节点为红色2.删除节点为黑色2.1兄弟节点为黑色&#xff0c;有孩子节点&am…...

联通数科如何基于Apache DolphinScheduler构建DataOps一体化能力平台

各位小伙伴晚上好&#xff0c;我是联通数字科技有限公司数据智能事业部的王兴杰。 今天&#xff0c;我将和大家聊一聊联通数字科技有限公司是如何基于Apache DolphinScheduler构建DataOps一体化能力平台的。 今天的分享主要分为三个部分&#xff1a; 关于DataOps的一些思考&a…...

Python知识点:如何使用Mitmproxy进行HTTP/HTTPS流量分析

Mitmproxy 是一个强大的中间人代理工具&#xff0c;可以用来分析和修改 HTTP 和 HTTPS 流量。以下是如何使用 Mitmproxy 进行 HTTP/HTTPS 流量分析的步骤&#xff1a; 安装 Mitmproxy 首先&#xff0c;你需要在系统上安装 Mitmproxy。可以通过以下方式安装&#xff1a; 使用 …...

06:【stm32】OLED模块的简单使用

OLED模块的简单使用 OLED简单的使用 OLED简单的使用 OLED驱动函数是使用B站UP江科大的。我们直接调用即可&#xff0c;是使用软件模拟I2C协议进行通信的。具体的I2C协议可查看上官嵌入式开发中的C51单片机开发。 驱动函数文件&#xff1a;通过百度网盘分享的文件&#xff1a;…...

HIVE4.0.0的10000端口启动不起来的一种情况

问题 原生态部署HIVE4.0.0启动不起来10000端口&#xff0c;也没找到日志文件的位置&#xff0c;后来才知道日志文件默认在/tmp/<hostname>/路径下面&#xff0c;查看日志以为是Tez没安装的问题&#xff0c;我这儿要实现hive on spark&#xff0c;是不是该安装spark然后启…...

[极客大挑战 2019]FinalSQL1

打开题目 sql注入&#xff0c;点击1试一下 点击2试一下 点击3试一下 点击4 点击5 id6试一下 感觉是sql盲注了 编写脚本 import requests import string from time import sleep url "http://9da9cb18-3096-413a-9476-8a177ffec31a.node4.buuoj.cn:81/search.php?id0^(…...

Go语言 标签Label

Go语言 label标签和枚举介绍及使用示例 目录 标签label 标签和goto continue break 枚举 代码示例 说明 总结 标签label 标签和goto 设置标签&#xff0c;并在标签中判断符合条件后&#xff0c;跳到指定标签位置。 示例如下&#xff1a; package mainimport "…...

自反射 RAG 管道:如何实现?

什么是 Self-RAG? 人工智能中的自反射 RAG(检索增强生成)管道是指一种自适应和自我改进的系统,它结合了信息检索和语言生成过程,以提供更准确和特定于上下文的响应。这种类型的管道超越了标准的RAG 管道,它结合了一种自反射机制,使其能够评估其性能,确定需要改进的领域…...

怎么将jar注册为windows系统服务详细操作

将spring boot项目编译成jar,注册为windows系统服务 在网上了解到,winsw这个开源项目,去github看了下,作者常年维护更新,文档齐全,拥有不少,自己写了个小demo体验了下还不错,然后又运行了一个晚上,没啥问题,遂决定采用它 开源地址 源库地址 https://github.com/winsw/winsw R…...

数据结构.

1:基本大纲 数据结构、算法线性表&#xff1a;顺序表、链表、栈、队列树&#xff1a;二叉树、遍历、创建查询方法、排序方式 2:数据结构&#xff08;逻辑结构&#xff0c;存储结构&#xff0c;操作&#xff08;数据的运算&#xff09;&#xff09; 2.1&#xff1a;数据&#xf…...

thinkphp5之sql注入漏洞-builder处漏洞

目录 适用版本 环境搭建 文件下载安装 配置文件修改 漏洞分析 适用版本 注&#xff1a;thinkphp版本&#xff1a;5.0.13<ThinkPHP<5.0.15 、 5.1.0<ThinkPHP<5.1.5 环境搭建 文件下载安装 在github上面下载相应版本&#xff0c;下载think文件&#xff0c;…...

30集 如何编写ESP32程序接入AIGC实现更多有趣的功能-《MCU嵌入式AI开发笔记》

30集 如何编写ESP32程序接入AIGC实现更多有趣的功能&#xff08;温度&#xff09;-《MCU嵌入式AI开发笔记》 前言 之前我们建立了ESP-IDF和ESP-ADF开发环境&#xff0c;验证了硬件&#xff0c;验证了AI-CHAT的AI聊天工程&#xff0c;并且深入学习了cmake编译过程&#xff0c;…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

scikit-learn机器学习

# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

ubuntu22.04有线网络无法连接,图标也没了

今天突然无法有线网络无法连接任何设备&#xff0c;并且图标都没了 错误案例 往上一顿搜索&#xff0c;试了很多博客都不行&#xff0c;比如 Ubuntu22.04右上角网络图标消失 最后解决的办法 下载网卡驱动&#xff0c;重新安装 操作步骤 查看自己网卡的型号 lspci | gre…...

数据库优化实战指南:提升性能的黄金法则

在现代软件系统中&#xff0c;数据库性能直接影响应用的响应速度和用户体验。面对数据量激增、访问压力增大&#xff0c;数据库性能瓶颈经常成为项目痛点。如何科学有效地优化数据库&#xff0c;提升查询效率和系统稳定性&#xff0c;是每位开发与运维人员必备的技能。 本文结…...

python学习day39

图像数据与显存 知识点回顾 1.图像数据的格式&#xff1a;灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 import torch import torchvision import torch.nn as nn imp…...

分类数据集 - 场景分类数据集下载

数据集介绍&#xff1a;自然场景分类数据集&#xff0c;真实场景高质量图片数据&#xff1b;适用实际项目应用&#xff1a;自然场景下场景分类项目&#xff0c;以及作为通用场景分类数据集场景数据的补充&#xff1b;数据集类别&#xff1a;buildings、forest、glacier、mounta…...

【从零学习JVM|第二篇】字节码文件

前言&#xff1a; 通过了解字节码文件可以帮助我们更容易的理解JVM的工作原理&#xff0c;所以接下来&#xff0c;我们来介绍一下字节码文件。 目录 前言&#xff1a; 正确的打开字节码文件 字节码文件组成 1. 魔数&#xff08;Magic Number&#xff09; 2. 版本号&…...

Java线程池核心原理与最佳实践

Java 线程池详解 线程池是Java并发编程的核心组件&#xff0c;它能高效管理线程生命周期&#xff0c;避免频繁创建销毁线程的开销&#xff0c;提升系统性能和资源利用率。 一、线程池核心优势 降低资源消耗&#xff1a;复用已创建的线程&#xff0c;减少线程创建销毁开销提高…...