14. Spring AOP(二)实现原理

源码位置:spring_aop

上一篇文章中我们主要学习了AOP的思想和Spring AOP使用,本文讲的是Spring是如何实现AOP的,Spring AOP是基于动态代理来实现AOP的,在将动态代理之前先来了解一下什么是代理模式。

1. 代理模式

在现实中就有许多代理的例子:比如你想找一个明星帮你拍广告,明星只对他的代理人开放权限,你就需要跟他的代理人去沟通,由代理人来代替明星完成收钱+售后的操作,然而拍广告这件事还是明星去做的。

1.1 代理模式的定义

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

解释: 在某些情况下,调用方不太适合直接访问目标对象的方法(没开放权限),就需要使用这种设计模式来使用一个代理对象在调用方和目标对象之间起到中介作用,代理模式在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强(Advice)。这里的增强和上一篇Spring AOP中的通知(Advice)是一样的,本章节将Advice全都统称为增强。

代理模式的主要角色:

  • Subject:业务接口类,可以是抽象类或者接口(不一定有)
  • RealSubject:目标对象(被代理对象),也就是具体的业务执行
  • Proxy:代理对象,RealSubject的代理

Untitled Diagram.drawio-2.png

1.2 代理模式的实现

代理模式的实现分为静态代理动态代理

1.2.1 静态代理

定义:在程序运行前,代理类的.class文件就已经存在了,也就是提前把所有方法的代理业务全都写好在程序里。

接下来我通过代码的方式,以明星接广告为例来加深理解:

  1. 定义接口(定义拍广告的业务)
public interface Subject {void takeAdvertise();
}
  1. 实现接口(由明星来实现拍广告这个业务)
public class RealSubject implements Subject{@Overridepublic void takeAdvertise() {System.out.println("我是明星,我来拍广告");}
}
  1. 实现代理(代理人,帮明星处理业务中的额外操作)
public class Proxy implements Subject{private final RealSubject realSubject;public Proxy(RealSubject realSubject) {this.realSubject = realSubject;}@Overridepublic void takeAdvertise() {System.out.println("我是代理人,开始收钱");realSubject.takeAdvertise();System.out.println("我是代理人,提供售后");}
}

测试代码:模拟代理模式下你通过Proxy(代理对象),访问目标对象(RealSubject)

public class Application {public static void main(String[] args) {Proxy proxy = new Proxy(new RealSubject());proxy.takeAdvertise();}
}

运行结果:

我是代理人,开始收钱
我是明星,我来拍广告
我是代理人,提供售后

这就是一个静态的代理模式的实现,静态代理模式的劣势也很明显,当你需要新增一个业务的时候,比如找明星开演唱会的时候,他的代理人做的事情同样是收钱+售后,你就需要继续编写下面代码:

  1. 定义接口:
public interface Subject {void takeAdvertise();void giveConcert();
}
  1. 实现接口:
public class RealSubject implements Subject{@Overridepublic void takeAdvertise() {System.out.println("我是明星,我来拍广告");}@Overridepublic void giveConcert() {System.out.println("我是明星,我来开演唱会");}
}
  1. 实现代理:
public class Proxy implements Subject{private final RealSubject realSubject;public Proxy(RealSubject realSubject) {this.realSubject = realSubject;}@Overridepublic void takeAdvertise() {System.out.println("我是代理人,开始收钱");realSubject.takeAdvertise();System.out.println("我是代理人,提供售后");}@Overridepublic void giveConcert() {System.out.println("我是代理人,开始收钱");realSubject.giveConcert();System.out.println("我是代理人,提供售后");}
}
  1. 运行结果:
我是代理人,开始收钱
我是明星,我来开演唱会
我是代理人,提供售后

这时候我们发现代理对象出现冗余代码,既然代理对象做的都是收钱+售后两件事,我们是否有办法消除这些冗余代码呢?这时候我们就要提到动态代理了。

1.2.2 动态代理

定义:在程序运行时,运用反射机制动态创建而成

相对于静态代理来说,它更加灵活:我们不需要针对每个目标对象业务的增加而去修改代理对象中的代码,而是把创建代理对象的工作推迟到程序运行时由JVM来实现,也就是说动态代理在程序的运行时,根据需要动态创建生成。

Java对动态代理进行了实现,并提供了一些API,常见的实现方式有两种:

  • JDK动态代理
  • CGLIB动态代理

JDK动态代理
  1. 编写代理类:通过JDK动态代理的提供的接口实现一个新的代理类
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("JDK动态代理开始");Object result = method.invoke(target, args);System.out.println("JDK动态代理结束");return result;}
}

解释一下代理类:

  • 代理类中同样需要一个目标对象target,这里我们使用Object得以接收不同的目标对象
  • 实现 java.lang.reflect.InvocationHandler接口,并实现invoke方法
  • invoke()方法的作用是调用目标方法并对方法进行增强,代理对象增强的内容和目标方法的逻辑在该方法的代码块中得以体现
  • invoke()方法中有三个参数Object proxy, Method method, Object[] args
    • proxy: 表示生成的代理对象,通常不直接使用
    • method: 是反射机制里的对象,该对象可以通过类的class对象+方法名来获取,通过该对象可以调用目标方法
    • args: 参数列表,用于传入目标方法中需要的参数
  1. 获取代理对象并使用:
public class Application {public static void main(String[] args) {//动态代理//目标类RealSubject target = new RealSubject();//根据代理类获取代理对象Subject proxy = (Subject) Proxy.newProxyInstance(target.getClass().getClassLoader(),new Class[] {Subject.class},new JDKInvocationHandler(target));proxy.giveConcert();proxy.takeAdvertise();}
}

解释下上述过程如何获取代理对象的:

获取代理对象需要使用java.lang.reflect.Proxy类中的newProxyInstance()方法,在该方法中需要传入三个参数:

  • 类加载器: 在使用动态代理时,通常建议传入目标对象的类加载器作为参数,以确保代理对象能够正常访问目标对象的方法。
  • Class数组: 这是一个包含目标对象所实现的接口的数组,代理对象将实现这些接口,并将方法调用委托给 JDKInvocationHandler(下一个参数)
  • InvocationHandler: 这是一个实现了 InvocationHandler 接口的对象,用于处理代理对象的方法调用。这里传入了一个JDKInvocationHandler 对象,它将拦截代理对象的方法调用,并在方法执行前后执行额外的逻辑。

JDK动态代理的局限性:代理类和目标类都必须实现相同的接口,因此只能代理接口类,如果把目标类为普通类的话就会报错,而CGLIB动态代理恰巧可以弥补这方面的不足,接下来我们就讲讲GCLIB。

CGLIB动态代理

CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,允许我们在运行时对字节码进行修改和动态生成,CGLIB通过继承方式实现代理。

  1. 导入GCLIB的依赖:
<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>
  1. 实现接口类:实现MethodInterceptor接口,使用起来和JDK动态代理差不多,这里就不赘述了。
public class CGLibMethodInterceptor implements MethodInterceptor {private Object target;public CGLibMethodInterceptor(Object target) {this.target = target;}/**** @param o 代理类* @param method 目标方法* @param objects 参数列表* @param methodProxy*/@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("CGLib动态代理开始");Object result = method.invoke(target, objects);System.out.println("CGLib动态代理结束");return result;}
}
  1. 这里我新创建一个不实现接口的普通目标对象
public class RealSubject2 {public void takeAdvertise() {System.out.println("我是明星,我来拍广告");}public void giveConcert() {System.out.println("我是明星,我来开演唱会");}
}
  1. 获取代理对象并使用,与JDK获取代理对象的差别如下:
  • JDK中必须使用一个接口类来接收代理对象,GCLib直接使用目标类来接收
  • 创建的时候调用net.sf.cglib.proxy.Enhancer类的creat()方法
  • create()方法中少传了一个class[]接口列表
public class Application {public static void main(String[] args) {//CGLIB动态代理RealSubject2 target = new RealSubject2();RealSubject2 proxy = (RealSubject2) Enhancer.create(target.getClass(),new CGLibMethodInterceptor(target));proxy.takeAdvertise();proxy.giveConcert();}
}
  1. 成功对普通目标对象的目标方法进行增强:
CGLib动态代理开始
我是明星,我来拍广告
CGLib动态代理结束
CGLib动态代理开始
我是明星,我来开演唱会
CGLib动态代理结束

2. Spring AOP 的实现

Spring AOP是通过动态代理实现的,上面我们提到的JDK动态代理和CGLIB动态代理,Spring AOP都用到了。

【问题】Spring AOP 什么时候使用JDK,什么时候又使用GCLIB呢?

Spring AOP通过代理工厂创建代理对象,代理工厂中有一个重要的属性:proxyTargetClass,它的值可以为true|false

proxyTargetClass目标对象代理方式
false实现了接口jdk代理
false未实现接口(只有实现类)cglib代理
true实现了接口cglib代理
true未实现接口(只有实现类)cglib代理

该属性是基于程序员配置的,Spring Framework中默认属性值为false,可以通过@EnableAspectJAutoProxy(proxyTargetClass = true)来修改为true。

Spring Boot项目中,@EnableAspectJAutoProxy注解无效,程序员可在application.yml中修改属性值,并且从Spring Boot 2.x 开始,该属性值默认为true,程序员可以在配置文件中将其修改为false

spring:aop:proxy-target-class: false

3. 总结

本篇文章首先介绍了一种新的设计模式 —— 代理模式,然后围绕代理模式介绍了静态代理和动态代理分别是什么。

Spring AOP是基于动态代理实现的,本文着重介绍了两种动态代理方式的差别:JDK动态代理和CGLIB动态代理;并且介绍了Spring AOP什么时候使用JDK代理和CGLIB动态代理。

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

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

相关文章

深度学习transformer架构详细详解

一、transformer的贡献 transformer架构的贡献&#xff1a;该架构只使用自注意力机制&#xff0c;没有使用RNN或卷积网络。且可以实现并行计算&#xff0c;加快模型训练速度。 &#xff08;将所有的循环层全部换成&#xff1a;multi-headed self-attention&#xff09; 二、t…

VisualStudio2019和2022开发Winform项目用到Devexpress组件报错不能正确加载的解决办法

1.报错1 问题简单描述&#xff1a;DevExpress.Utils.ImageCollectionStreamer 无法强制转换为 DevExpress.Utils.ImageCollectionStreamer。 原因分析&#xff1a;原项目某个组件使用的是 DevExpresss.XtraBars.v15.1版本&#xff0c;直接引用扩展控件改成引用v20.2。 解决办法…

负载均衡集群——Nginx

目录 1.Nginx反向代理实战 2.Nginx 反向代理和负载均衡实践 实验操作步骤 步骤 1 Nginx1 和 Nginx2 配置 步骤2 测试资源是否可用 步骤 3 安装和配置 Nginx 代理 步骤 4 代理服务器配置检测 步骤 5 在 Nginx1 和 Nginx2 配置虚拟主机 步骤 6 将虚拟主机添加入后端主机组…

BMR:基于Boostrapping多视图的虚假新闻检测

一、概述 文章提出了三种视图信息来表示一篇新闻&#xff1a;文本、图像结构、图像语义。然后设计了改进的多门混合专家系统&#xff08;iMMoE&#xff09;来进行信息融合。保留单模态信息来保证特征对新闻的保真性&#xff0c;增加的多模态信息能保证不同模态的一致性&#xf…

【KingSCADA】通过地址引用和弹窗模板实现设备控制

当相同的设备过多时&#xff0c;要做很多相同的弹窗&#xff0c;这种情况下可以通过地址引用和弹窗模板实现设备控制。 1.变量创建 2.画面开发 以阀门控制为例&#xff0c;只需要做一个阀门控制界面模板 3.地址引用 # 4.实现效果

每日算法4/21

LCR 073. 爱吃香蕉的狒狒 题目 狒狒喜欢吃香蕉。这里有 N 堆香蕉&#xff0c;第 i 堆中有 piles[i] 根香蕉。警卫已经离开了&#xff0c;将在 H 小时后回来。 狒狒可以决定她吃香蕉的速度 K &#xff08;单位&#xff1a;根/小时&#xff09;。每个小时&#xff0c;她将会选…

web自动化系列-selenium的3种弹框操作(十二)

在进行功能测试时 &#xff0c;经常会遇到出现各种的弹出的提示 &#xff0c;比如删除数据给出提示 、做某个操作时也会弹框给出一些友好提示 &#xff0c;因为这些弹框都是做web操作时的一些常用组件 &#xff0c;所以&#xff0c;selenium就不得不支持这些组件 。 1.弹框介绍…

随机森林计算指标重要性—从决策树到随机森林Python实现

文章目录 前言一、节点二、决策树2.1 案例分析——优良的水稻2.2 案例分析——家庭财富水平 三、随机森林三、Python代码实现3.1 关键问题3.1.1 节点的表示3.1.2 决策树的表示** 根节点划分左右子树的依据 **3.1.3 随机森林的构造与重要性的表示 3.2 节点类3.2 决策树类3.2.1 初…

安装Fake UserAgent 库的方法最终解答!_Python库

安装Python库Fake UserAgent 我的环境&#xff1a;Window10&#xff0c;Python3.7&#xff0c;Anaconda3&#xff0c;Pycharm2023.1.3 Fake UserAgent Fake UserAgent 是一个Python库&#xff0c;用于生成随机或特定的用户代理&#xff08;UserAgent&#xff09;字符串。用户…

明日周刊-第7期

转眼间就又快到了五一假期&#xff0c;小长假有什么计划吗。封面配图是杭州高架上的月季花&#xff0c;非常好看。 文章目录 一周热点资源分享言论歌曲推荐 一周热点 鸿蒙系统持续扩大影响力&#xff1a;近期&#xff0c;华为官方宣布广东省已有超过600款应用加入鸿蒙系统&…

【自用】个人の画版规范

供电 总结起来就是&#xff1a;从正面看。从左到右的顺序是 VCC GND VEE&#xff0c;若是单电源则是VEE GND GND。 尽量用3p的。 XH2.54 接线端子

每天五分钟机器学习:神经网络模型参数的选择

本文重点 在深度学习和人工智能的浪潮中,神经网络作为其中的核心力量,发挥着举足轻重的作用。然而,神经网络的性能并非一蹴而就,而是需要经过精心的参数选择和调优。 神经网络由大量的神经元组成,每个神经元之间通过权重进行连接。这些权重,以及神经元的偏置、激活函数…

9.Eureka服务发现+Ribbon+RestTemplate服务调用

order-service服务通过服务名称来代替 ip:port的方式访问user-service服务的接口。 原来的请求代码&#xff1a; Service public class OrderServiceImpl implements OrderService {Autowiredprivate OrderMapper orderMapper;Autowiredprivate RestTemplate restTemplate;Ov…

PHP反序列化漏洞原理(附带pikachu靶场演示)

1.反序列化概念 序列化:是将变量转换为可保存或传输的字符串的过程;实现函数是serialize()反序列化:就是在适当的时候把这个字符串再转化成原来的变量使用&#xff0c;就是序列化的逆过程。实现函数是unserialize() 直白一点就是&#xff1a;序列化是把对象转换成字节流&#…

SpringAOP从入门到源码分析大全(四)SpringAOP的源码分析

文章目录 系列文档索引六、EnableAspectJAutoProxy源码分析1、AnnotationAwareAspectJAutoProxyCreator源码&#xff08;1&#xff09;wrapIfNecessary方法&#xff08;2&#xff09;createProxy 2、getAdvicesAndAdvisorsForBean查找所有Advisor&#xff08;1&#xff09;find…

深入理解CAS机制-基础使用与三大问题

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. 原子性问题 3. 乐观锁与悲观锁 4. CAS操作 5. CAS算法带来的三大…

Dynamic Wallpaper for Mac激活版:视频动态壁纸软件

Dynamic Wallpaper for Mac 是一款为Mac电脑量身打造的视频动态壁纸应用&#xff0c;为您的桌面带来无限生机和创意。这款应用提供了丰富多样的视频壁纸选择&#xff0c;涵盖了自然风景、抽象艺术、科幻奇观等多种主题&#xff0c;让您的桌面成为一幅活生生的艺术画作。 Dynami…

【教程】MySQL数据库学习笔记(五)——约束(持续更新)

写在前面&#xff1a; 如果文章对你有帮助&#xff0c;记得点赞关注加收藏一波&#xff0c;利于以后需要的时候复习&#xff0c;多谢支持&#xff01; 【MySQL数据库学习】系列文章 第一章 《认识与环境搭建》 第二章 《数据类型》 第三章 《数据定义语言DDL》 第四章 《数据操…

Linux基本命令之正则表达式(转义字符)

一&#xff1a;查看二进制文件 strings 命令&#xff1a;strings 文件名 生成链接文件 ln 命令&#xff1a;ln 选项 源文件(f1) 链接文件&#xff08;f2&#xff09; 软连接&#xff1a;eg:ln -s f1 f2 软链接不能跨分区链接&#xff0c;但可以在同一分区的不同目录下链接…

go+react实现远程vCenter虚拟机管理终端

文章目录 React-VcenterDemoQuick Start React-Vcenter 基于go & react实现远程vSphere vcenter虚拟机终端console页面&#xff0c;提供与vcenter管理中的Launch Web Console相同的功能。 项目地址&#xff1a;react-vcenter Demo URL: http://localhost:3000 Quick St…