15. Spring AOP 的实现原理 代理模式

目录

1. 代理模式

2. 静态代理 

3. 动态代理

3.1 JDK 动态代理

3.2 CGLIB 动态代理

4. JDK 动态代理和 CGLIB 动态代理对比

5. Spring代理选择

6. Spring AOP 实现原理

6.1 织入

7. JDK 动态代理实现 

8. CGLIB 动态代理实现

9. 总结


1. 代理模式

代理模式:为其他对象提供⼀种 代理 以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另⼀个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式分为静态代理动态代理。 

2. 静态代理 

静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活(比如接口⼀旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写⼀个代理类)。 实际应用场景非常少,日常开发几乎看不到使用静态代理的场景。
上面我们是从实现和应用角度来说的静态代理,从 JVM 层面来说, 静态代理在编译时就将接⼝、实现类、代理类这些都变成了⼀个个实际的 class 文件

静态代理实现步骤:

  1. 定义⼀个接口及其实现类;
  2. 创建⼀个代理类同样 实现这个接口;
  3. 将目标对象注入进代理类,然后在代理类的对应方法调用目标类中的对应方法。

这样,我们就可以通过代理类屏蔽目标对象的访问,并且可以在目标方法执行前后实现其他的功能。接下来,我们来看一下具体是如何实现静态代理的:

1. 定义接口

2. 实现接口

3. 创建代理类并同样实现支付接口

4. 实际使用

3. 动态代理

相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。

从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中 的。
Spring AOP 的实现依赖了动态代理。
就 Java 来说,动态代理的实现方式有很多种,比如 JDK 动态代理CGLIB 动态代理等等。

3.1 JDK 动态代理

在 Java 动态代理机制中 InvocationHandler 接口 Proxy 类 是核心。
JDK 动态代理类步骤:
  1. 定义⼀个接口及其实现类;
  2. 自定义 InvocationHandler 并重写 invoke 方法 ,在 invoke 方法中会调用原生方法(被代理类的方法)并自定义⼀些处理逻辑;
  3. 通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[]
    interfaces,InvocationHandler h) 方法创建代理对象;

定义 JDK 动态代理类:

创建一个代理对象并使用:

Proxy 类中使用频率最高的方法是:newProxyInstance() ,这个方法主要用来生成⼀个代理对象。

这个方法⼀共有 3 个参数:
  1.  loader :类加载器,用于加载代理对象。
  2.  interfaces : 被代理类实现的⼀些接口;
  3.  h : 实现了 InvocationHandler 接口的对象; 

3.2 CGLIB 动态代理

JDK 动态代理有⼀个最致命的问题是其只能代理实现了接口的类。

 因此,可以通过 CGLIB 动态代理机制来避免。

CGLIB(Code Generation Library)是⼀个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。很多知 名的开源框架都使用到 CGLIB,例如 Spring 的 AOP 模块中:如果目标对象 实现了接口 ,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理
在 CGLIB 动态代理机制中 MethodInterceptor 接口 Enhancer 类 是核心。

CGLIB 动态代理类使用步骤:

  1. 定义⼀个类;
  2. ⾃定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
  3. 通过 Enhancer 类的 create()创建代理类。

添加依赖:

和JDK 动态代理不同, CGLIB(Code Generation Library) 实际是属于⼀个开源项目,使用时需要手动添加相关依赖。

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

定义 MethodInterceptor(方法拦截器):

创建代理类, 并使用:

自定义 MethodInterceptor 并重写 intercept 方法, intercept 用于拦截增强被代理类的方法

  1. obj : 被代理的对象(需要增强的对象)
  2. method : 被拦截的方法(需要增强的方法)
  3. args : 方法入参
  4. proxy : 用于调用原始方法

4. JDK 动态代理和 CGLIB 动态代理对比

  1. JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。
  2. CGLIB 动态代理是通过生成⼀个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final。
性能: 大 部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

5. Spring代理选择

  1. proxyTargetClass 为 false, 目标实现了接口, 用 jdk 代理
  2. proxyTargetClass 为 false, 目标未实现接口, 用 cglib代理
  3. proxyTargetClass 为 true, 用 cglib 代理

6. Spring AOP 实现原理

Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的支持局限于方法级别的拦截。
Spring AOP 支持 JDK Proxy 和 CGLIB 方式实现动态代理。默认情况下, 实现了接口的类,使
用 AOP 会基于 JDK 生成代理类;没有实现接口的类,会基于 CGLIB 生成代理类。

6.1 织入

织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对
象中。
在目标对象的生命周期里有多个点可以进行织入:
  • 编译期:切⾯在⽬标类编译时被织⼊。这种⽅式需要特殊的编译器。AspectJ的织⼊编译器就
    是以这种⽅式织⼊切⾯的。
  • 类加载期:切⾯在⽬标类加载到JVM时被织⼊。这种⽅式需要特殊的类加载器 (ClassLoader),它可以在⽬标类被引⼊应⽤之前增强该⽬标类的字节码。AspectJ5的加载 时织⼊(load-time weaving. LTW)就⽀持以这种⽅式织⼊切⾯。
  • 运行期:切面在应用运行的某一时刻被织入。一般情况下,在织入切面时,AOP 容器会为目标对象动态创建一个代理对象。Spring AOP 就是以这种方式织入切面的。

7. JDK 动态代理实现 

public interface PayService {void pay();
}
public class AliPayService implements PayService{@Overridepublic void pay(){System.out.println("ali pay...");}
}
public class PayServiceJDKInvocationHandler implements InvocationHandler {// 目标对象就是被代理对象private Object target;public PayServiceJDKInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("安全检查");System.out.println("记录日志");System.out.println("记录开始时间");// 通过反射调用被代理的方法Object retVal = method.invoke(target,args);System.out.println("记录结束时间");return retVal;}public static void main(String[] args) {PayService target = new AliPayService();// 方法调用处理器InvocationHandler handler = new PayServiceJDKInvocationHandler(target);// 创建一个代理类:通过被代理类、被代理实现的接口、方法调用处理器来创建PayService proxy =(PayService) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[]{PayService.class},handler);proxy.pay();}
}

8. CGLIB 动态代理实现

public interface PayService {void pay();
}
public class PayServiceCGLIBInterceptor implements MethodInterceptor {// 被代理对象private Object target;public PayServiceCGLIBInterceptor(Object target) {this.target = target;}@Overridepublic Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {System.out.println("安全检查");System.out.println("记录日志");System.out.println("记录开始时间");// 通过反射调用被代理的方法Object retVal = methodProxy.invoke(target,args);System.out.println("记录结束时间");return retVal;}public static void main(String[] args) {PayService target = new AliPayService();PayService proxy =(PayService) Enhancer.create(target.getClass(),new PayServiceCGLIBInterceptor(target));proxy.pay();}
}

9. 总结

AOP 是对某方面能力的统⼀实现,它是⼀种实现思想,Spring AOP 是对 AOP 的具体实现,Spring AOP 可通过 AspectJ(注解)的方式来实现 AOP 的功能,Spring AOP 的实现步骤是:
  1. 添加 AOP 框架支持;
  2. 定义切面和切点;
  3. 定义通知。
Spring AOP 是通过动态代理的方式,在运行期将 AOP 代码织入到程序中的,它的实现方式有两种: JDK Proxy 和 CGLIB。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/16113.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac查看系统状态

syatem profiler mac系统中提供了system profiler来查看系统的详细信息&#xff0c;包括硬件、网络以及安装的软件 Console 显示了系统上的日志文件信息&#xff0c;有助于诊断问题 Activity Monitor 可以提供正在运行的系统的相关信息 https://zhhll.icu/2021/Mac/查看系统…

【云原生】一文学会Docker存储所有特性

目录 1.Volumes 1.Volumes使用场景 2.持久将资源存放 3. 只读挂载 2.Bind mount Bind mounts使用场景 3.tmpfs mounts使用场景 4.Bind mounts和Volumes行为上的差异 5.docker file将存储内置到镜像中 6.volumes管理 1.查看存储卷 2.删除存储卷 3.查看存储卷的详细信息…

Java课题笔记~Maven基础

2、Maven 基础 2.1 Maven安装与配置 下载安装 配置&#xff1a;修改安装目录/conf/settings.xml 本地仓库&#xff1a;存放的是下载的jar包 中央仓库&#xff1a;要从哪个网站去下载jar包 - 阿里云的仓库 2.2 创建Maven项目

MySQL数据库 【索引事务】

目录 一、概念 二、索引的优缺点 1、索引的优点 2、索引的缺陷 三、索引的使用 1、查看索引 2、创建索引 3、删除索引 四、索引底层的数据结构 1、B树 2、B树 五、索引事务 1、概念和回滚 2、事务的使用 3、事务的基本特性 4、并发会遇到的问题 &#xff08…

jenkins执行jmeter时,报Begin size 1 is not equal to fixed size 5

jenkins执行jmeter脚本的时候一直提示如下错误&#xff1a; Tidying up ... Fri Jul 28 17:03:53 CST 2023 (1690535033178) Error generating the report: org.apache.jmeter.report.dashboard.GenerationException: Error while processing samples: Consumer failed wi…

游游的排列构造

示例1 输入 5 2 输出 3 1 5 2 4 示例2 输入 5 3 输出 2 1 4 3 5 #include<bits/stdc.h> using namespace std; typedef long long ll; const int N1e55; int n,k; int main(){scanf("%d%d",&n,&k);int xn-k1;int yn-k;int f1;for(int i1;i&l…

产品经理如何平衡用户体验与商业价值?

近期负责前端产品设计工作的小李忍不住抱怨&#xff1a;公司总是要求客户第一&#xff0c;实现客户良好体验&#xff0c;但在实际操作过程中&#xff0c;面向用户 体验提升的需求&#xff0c;研发资源计划几乎很难排上&#xff0c;资源都放在公司根据业务价值排序的需求…

大家做性能测试都用什么工具

在进行测试时&#xff0c;选择适合的测试工具至关重要&#xff0c;因为优秀的测试工具能够显著提高工作效率。对于性能测试和自动化测试而言&#xff0c;大多数人会选择传统的JMeter等工具&#xff0c;然而这些工具存在学习成本高、使用门槛高的问题。 因此&#xff0c;我在这…

intellij 编辑器内性能提示

介绍 IntelliJ IDEA已经出了最新版的2023.2&#xff0c;最耀眼的功能无法两个 AI Assistant编辑器内性能提示 AI Assistant 已经尝试过了是限定功能&#xff0c;因为是基于open ai,所以限定的意思是国内无法使用&#xff0c;今天我们主要介绍是编辑器内性能提示 IntelliJ Pr…

Flink回撤流

1.回撤流定义&#xff08;RetractStream&#xff09; Flink 的回撤流是指在 Flink 的流处理算法中&#xff0c;撤回已经发送到下游节点的数据。这是因为在实际应用场景中&#xff0c;有些错误数据可能会发送到下游节点&#xff0c;因此需要回撤流以保证数据的准确性。 回撤流…

EP4CE6E22C8N Error: Can‘t recognize silicon ID for device 1

经过各种排查&#xff0c;发现是AS配置不对&#xff0c;仅供参考 工程 参考某处的工程画板配置的FPGA板子&#xff0c;用于学习入门FPGA。 烧录sof文件是正常的&#xff0c;并能正常运行。 但是烧录jic是failed&#xff0c;查看报错为&#xff1a;Error: Can’t recognize si…

渗透测试:Linux提权精讲(二)之sudo方法第二期

目录 写在开头 sudo expect sudo fail2ban sudo find sudo flock sudo ftp sudo gcc sudo gdb sudo git sudo gzip/gunzip sudo iftop sudo hping3 sudo java 总结与思考 写在开头 本文在上一篇博客的基础上继续讲解渗透测试的sudo提权方法。相关内容的介绍与背…

1004. 最大连续1的个数 III

题目描述&#xff1a; 主要思路&#xff1a; 刚看到这个问题首先想到的是二分答案&#xff0c;二分长度&#xff0c;然后利用滑动窗口判断是否可以达成。 class Solution { public:bool find(int x,vector<int> nums, int k){int now0;for(int i0,j0;i<nums.size();…

根据端口号查找服务位置

已知服务的IP和端口&#xff0c;查找该服务所在位置 1、打开命令提示符&#xff08;CMD&#xff09; WINR快捷键打开运行对话框&#xff0c;输入CMD&#xff0c;打开命令行。 2、找到对应的PID或程序名称 输入netstat -ano|findstr 端口号&#xff0c;找到对应的PID&#…

解决:h5的<video>在移动端浏览器无法自动播放

并不是所有的移动端浏览器都无法自动播放&#xff0c;下载谷歌、火狐、edge等都可以正常播放&#xff0c;目前发现夸克浏览器无法自动播放。即autoplay属性失效。 <video autoplay"autoplay"></video> 可能移动端有移动端的策略&#xff0c;但解决夸克…

企业数字化转型失败率达80%,面临哪些挑战?应该如何规划?

随着数字化在社会的飞速发展&#xff0c;人们的生活工作娱乐等方方面面都已经被数字化占领&#xff0c;数字化所衍生出的数字经济更是成为高速增长的国民经济支柱&#xff0c;而数据作为“副产品”也成功进化为第五大生产要素&#xff0c;发挥出巨大的价值&#xff0c;变成了个…

白盒测试和黑盒测试的区别是什么?

前言 曾言道“黑猫&#xff0c;白猫&#xff0c;只要能抓住老鼠就是好猫”。我们的测试亦是如此&#xff0c;不管是黑盒测试还是白盒测试&#xff0c;只要能测试出来bug&#xff0c;可以找出问题所在&#xff0c;保障软件质量就是好的测试方法。 对于刚入门的软件测试小白来说…

Vue2封装自定义全局Loading组件

前言 在开发的过程中&#xff0c;点击提交按钮&#xff0c;或者是一些其它场景总会遇到Loading加载框&#xff0c;PC的一些UI库也没有这样的加载框&#xff0c;无法满足业务需求&#xff0c;因此可以自己自定义一个&#xff0c;实现过程如下。 效果图 如何封装&#xff1f; 第…

cicd实验

系列文章目录 文章目录 系列文章目录一、1.2. 二、安装并使用1.安装gitlab2.//Jenkins安装3. 总结 一、 1. 2. 二、安装并使用 需要三台服务器一台安装gitlab 192.168.169.10 第二台负责 安装jenkins 192.168.169.20 第三台是负责业务 192.168.169.30 1.安装gitlab yum in…

SpringBoot使用PropertiesLauncher加载外部jar包

启用SpringBoot的PropertiesLauncher 使用SpringBoot的PropertiesLauncher可以优先加载外部的jar文件, 这样可以在程序运行前替换jar包, 官方文档: Launching Executable Jars 使用演示 建立一个SpringBoot工程, 工程中依赖一个叫自定义的utils包, 版本是1.0.0, 通过http接口…