13. Spring AOP(一)思想及使用

1. 什么是Spring AOP

AOP的全称是Aspect Oriented Programming,也就是面向切面编程,是一种思想。它是针对OOP(面向对象编程)的一种补充,是对某一类事情的集中处理。比如一个博客网站的登陆验证功能,在用户进行新增、编辑、删除博客等操作前都需要进行用户的登陆验证,我们在对这些业务编码时,都需要考虑一下用户的登录验证。对于这种功能统一,且使用较多的功能,就可以考虑通过AOP来统一处理了。 引入AOP的思想之后,我们在处理其他业务的时候就不需要再考虑如登录验证这样的其他功能。

Spring AOP是Spring公司针对AOP思想提供的一种实现方式,除了登录验证的功能之外,Spring AOP还可以实现:统一日志记录、统一返回格式设置、统一异常处理等。

2. AOP的基本术语

2.1 切面(Aspect)

切面相当于AOP实现的某个功能的集合,比如说登录验证功能,切面是由切点(Pointcut)和通知(Advice)组成的

2.2 连接点(Join Point)

连接点是应用执行过程中能够插入切面的一个点。比如说一个博客系统,包含许多业务,其中可以插入切面的业务都可以称为连接点。

2.3 切点(Pointcut)

Pointcut的作用就是提供一组规则来匹配连接点(Join Point),比如说对于博客系统,它有一些url是不需要做登录验证功能的,比如注册业务,通过切点提供的这么一组规则,在不需要登录验证的地方,它就不会进行登录验证。

2.4 通知(Advice)

通知是切面要完成的工作,它定义了切面的具体实现是什么、何时使用,描述了切面要完成的工作以及何时执行这个宫欧的问题。Spring切面类中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后会通知本方法进行调用:

  • 前置通知(@Before):在目标方法调用之前执行。
  • 后置通知(@After):在目标方法返回或抛出异常后调用。
  • 返回之后通知(@AfterReturning):在目标方法返回后调用。
  • 抛异常后通知(@AfterThrowing):在目标方法抛出异常后调用。
  • 环绕通知(@Around):通知包裹了目标方法,在被目标方法执行之前和调用之后执行自定义的行为。

2.5 图解

以用户的登录验证为例,用图来让大家更好的理解上述定义体现的思想:

Untitled Diagram.drawio-3.png

接下来我们就来对面向切面编程的思想进行实现,如果看到这还是没有很理解AOP的思想的话,可以结合后面的实现代码再来看看这张图表达的意思。

3. Spring AOP 使用

Spring AOP的使用大体分为下面四步:

  1. 添加 Spring AOP的依赖
  2. 连接点方法的编写
  3. 定义切面和通知
  4. 定义切点

3.1 原生Maven项目中Spring AOP的使用

Spring AOP同样有两种实现方式,一种是使用xml配置的方式,一种是使用注解的方式。

在这里我将在原生的Maven项目中使用两种方式来实现搭乘地铁的业务,使用AOP的方式编写一个切面,并在切面里运用五种通知来实现搭乘地铁业务的安全检查(前置通知)、刷卡进出站(环绕通知)、通知异常(异常通知)、到达通知(返回后通知)、记录行程(后置通知)

3.1.1 xml方式使用Spring AOP

源码位置:spring-aop

1. 添加依赖

<dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.9.RELEASE</version>
</dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.2.9.RELEASE</version>
</dependency>

2. 连接点方法的编写:在service包下新建一个SubwayService来实现乘坐地铁的业务

public class SubwayService {public void takeSubway() {System.out.println("乘坐地铁,行驶中...");//int n = 10/0;}
}

3. 定义切面类和通知方法:在aspect包下新增一个SubwayAspect类,并在里面编写对应的通知方法

public class SubwayAspect {public void securityCheckAdvice() {System.out.println("前置通知:开始安全检查");}public void recordAdvice() {System.out.println("后置通知:记录本次行程");}public void expressionAdvice() {System.out.println("异常通知:运行过程出现异常");}public void swipeAdvice(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("环绕通知start:开始并刷卡进站");joinPoint.proceed();System.out.println("环绕通知finish:结束并刷卡出站");}public void arriveDestination() {System.out.println("返回后通知:到达目的地");}
}

注意事项:swipeAdvice()是环绕通知的方法,由于其是环绕通知,因此会在连接点方法开始前和结束后的时候分别执行不同的逻辑,因此需要使用一个ProceedingJoinPoint的对象来对应连接点的方法,并使用proceed()来执行连接点方法,分别在joinPoint.proceed();语句执行的前后编写编写环绕通知。

光编写完切面和通知还没什么用,还需要在xml文件中配置才行。

配置schema路径,直接复制即可:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"></beans>

将类注册到Spring容器中,这一步不多赘述:

<bean id="subwayAspect" class="com.chenshu.xml_aop.aspect.SubwayAspect"></bean>
<bean id="subwayService" class="com.chenshu.xml_aop.service.SubwayService"></bean>

配置AspectAdvice

  1. <beans>标签内添加一对<aop:config>的标签,有关aop的配置都放在这里面
  2. <aop:config>标签内添加一对<aop:aspect>标签,id属性的值自己定义,用于标识一个切面的idref属性里面的值对应前面注册入Spring的Aspect类的beanid
  3. <aop:aspect>标签内配置通知方法,不同的通知方法对应不同的标签,其中有method属性对应前面编写的通知方法的方法名,以及一个pointcut-ref来定义该通知的切点,由于我们的切点还未定义,因此这里用一个"?"替代
<aop:config><aop:aspect id="subwayAspect" ref="subwayAspect"><aop:before method="securityCheckAdvice" pointcut-ref="?"/><aop:after method="recordAdvice" pointcut-ref="?"/><aop:after-throwing method="expressionAdvice" pointcut-ref="?"/><aop:around method="swipeAdvice" pointcut-ref="?"/><aop:after-returning method="arriveDestination" pointcut-ref="?"/></aop:aspect></aop:config>

4. 编写切点:这里我们就要根据切点表达式来创建一个切点用于描述一组匹配规则

【引入】切点表达式

一个切点表达式就是形如上面expression属性中的内容,切点表达式中可以包括以下内容:

  • execution(表达式前缀)
  • 权限修饰符(如public、private)
  • 方法返回类型(如void、String)
  • 包名
  • 类名
  • 方法名
  • 方法的参数列表

下面是一些切点表达式的示例,可自行结合上面内容以理解:

  • execution(public * com.example.service.SomeService.*(..)):匹配 com.example.service.SomeService 类中所有 public 方法。
  • execution(public void com.example.service.SomeService.*(..)):匹配 com.example.service.SomeService 类中所有 public void 方法。
  • execution(* com.example.service.*.*(..)):匹配 com.example.service 包下所有类的所有方法。
  • execution(* com.example..*.*(..)):匹配 com.example 包下、子孙包下所有类的所有方法
  • execution(* com.example.service.SomeService.*(..)):匹配 com.example.service.SomeService 类的所有方法。
  • execution(* com.example.service.SomeService.*(String)):匹配 com.example.service.SomeService 类中接受一个 String 类型参数的所有方法。
  • execution(* *(..)):匹配任何类中的任何方法。

了解了切点表达式后我们就可以编写切点了:

<aop:pointcut id="takeSubway" expression="execution(* com.chenshu.xml_aop.service.SubwayService.takeSubway())"/>

该切点的名字为takeSubway,切点表达式的意思是匹配com.chenshu.xml_aop.service.SubwayService这个类下的名为takeSubway无传入参数的方法。

然后我们就可以在通知中填入pointcut-ref属性的值了,完整的aop配置如下:

<aop:config><aop:pointcut id="takeSubway" expression="execution(* com.chenshu.xml_aop.service.SubwayService.takeSubway())"/><aop:aspect id="subwayAspect" ref="subwayAspect"><aop:before method="securityCheckAdvice" pointcut-ref="takeSubway"/><aop:after method="recordAdvice" pointcut-ref="takeSubway"/><aop:after-throwing method="expressionAdvice" pointcut-ref="takeSubway"/><aop:around method="swipeAdvice" pointcut-ref="takeSubway"/><aop:after-returning method="arriveDestination" pointcut-ref="takeSubway"/></aop:aspect></aop:config>

编写测试类进行测试:

public class Application {public static void main(String[] args) {ApplicationContext context =new ClassPathXmlApplicationContext("spring-aop.xml");SubwayService subwayService  =context.getBean("subwayService", SubwayService.class);subwayService.takeSubway();}
}
【总结】不同通知的执行顺序

在没有发生异常的情况下结果如下,通过结果可以了解不同通知的执行顺序:

前置通知:开始安全检查
环绕通知start:开始并刷卡进站
乘坐地铁,行驶中...
返回后通知:到达目的地
环绕通知finish:结束并刷卡出站
后置通知:记录本次行程

由于没有出现异常因此看不到抛异常后的通知

制造一个算数异常:

public class SubwayService {public void takeSubway() {System.out.println("乘坐地铁,行驶中.。。");int n = 10/0;}
}

发生异常的情况下结果如下,通过结果可以了解不同通知的执行顺序:

前置通知:开始安全检查
环绕通知start:开始并刷卡进站
乘坐地铁,行驶中...
异常通知:运行过程出现异常
后置通知:记录本次行程

由于方法执行一半就抛出异常,因此没有返回后的通知以及环绕通知的后半段

3.1.2 注解实现使用Spring AOP

源码位置:spring-aop_2

谈到Spring AOP的注解,就不得不谈到Spring AOP和AspectJ的关系:Spring AOP 的注解是基于 AspectJ 注解的一种简化和封装。这意味着你可以使用 AspectJ 注解来定义切面,但实际的织入过程是由 Spring AOP 来完成的。

添加依赖和xml方式是一样的,这里就不赘述了。

注解的方式只需要在xml中添加下面两个标签:上面是组件注解的扫描路径,下面是aspectj注解的声明

<context:component-scan base-package="com.chenshu.aop_annotation"/>
<aop:aspectj-autoproxy/>

编写Aspect类:

  1. Aspect类上添加@Aspect注解
  2. 定义一个方法,并在方法上使用@Pointcut注解将其声明一个切点,并在注解内添加value属性的值(切点表达式)
  3. 在通知方法上使用@Before(前置通知)、@After(后置通知)、@AfterThrowing(抛异常后通知)、@Around(环绕通知)、@AfterReturning(返回后的通知)注解声明不同类型的通知,并在注解属性中添加上一步定义的切点"myPointcut()"
@Component
@Aspect
public class SubwayAspect {@Pointcut(value = "execution(* com.chenshu.aop_annotation.service.SubwayService.takeSubway())")private void myPointcut() {}@Before("myPointcut()")public void securityCheckAdvice() {System.out.println("前置通知:开始安全检查");}@After("myPointcut()")public void recordAdvice() {System.out.println("后置通知:记录本次行程");}@AfterThrowing("myPointcut()")public void expressionAdvice() {System.out.println("异常通知:运行过程出现异常");}@Around("myPointcut()")public void swipeAdvice(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("环绕通知start:开始并刷卡进站");joinPoint.proceed();System.out.println("环绕通知finish:结束并刷卡出站");}@AfterReturning("myPointcut()")public void arriveDestination() {System.out.println("返回后通知:到达目的地");}
}

3.2 Spring Boot项目中Spring AOP的使用

源码位置:MyBatis_demo

这里我直接基于上一篇文章中的代码来演示Spring AOP的使用。

1. 添加依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2. 新建一个aspect包编写切面类LoginAspect

@Component
@Aspect
public class LoginAspect {@Pointcut("execution(* com.chenshu.mybatis_demo.controller.UserController.*(..))")public void myPointcut() {}@Before("myPointcut()")public void before() {System.out.println("进行登录验证");}
}

3. 测试切面是否生效:

由于我编写的切面中的前置方法对所有UserController类下的方法都生效,这里我直接访问一下UserControllergetUsers方法的路由"/getall"

image.png

查看日志信息:我们发现在执行getUsers()方法之前成功执行了前置通知

进行登录验证
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3e84be0f] was not registered for synchronization because synchronization is not active
2024-04-20 17:07:40.813  INFO 40289 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-04-20 17:07:40.890  INFO 40289 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@212054083 wrapping com.mysql.cj.jdbc.ConnectionImpl@3ef56d86] will not be managed by Spring
==>  Preparing: select * from userinfo
==> Parameters: 
<==    Columns: id, username, password, photo, createtime, updatetime, state
<==        Row: 1, zhang, 12345, doge.png, 2024-04-19 13:09:45, 2024-04-19 13:09:45, 1
<==        Row: 2, lisi, 123, , 2024-04-19 13:31:01, 2024-04-19 13:31:01, 1
<==        Row: 3, wangwu, 123, , 2024-04-19 14:17:29, 2024-04-19 14:17:29, 1
<==      Total: 3

4. 小结

本篇文章描述了AOP(面向切面编程)思想的定义,并讲解了四个基本术语连接点、切面、切点、通知的关系;然后又分别以xml配置文件和AspectJ注解的方式使用了Spring AOP。

下一篇文章将讲解有关Spring AOP的原理的内容。

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

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

相关文章

算法设计优化——有序向量二分查找算法与Fibonacci查找算法

文章目录 0.概述1.语义定义2. 二分查找&#xff08;版本A&#xff09;2.1 原理2.2 实现2.3 复杂度2.4 查找长度 3.Fibonacci查找3.1 改进思路3.2 黄金分割3.3 实现3.4 复杂度分析3.5 平均查找长度 4. 二分查找&#xff08;版本B&#xff09;4.1 改进思路4.2 实现4.3 性能4.4 进…

YOLOv8常见水果识别检测系统(yolov8模型,从图像、视频和摄像头三种路径识别检测)

1.效果视频&#xff08;常见水果识别&#xff08;yolov8模型&#xff0c;从图像、视频和摄像头三种路径识别检测&#xff09;_哔哩哔哩_bilibili&#xff09; 资源包含可视化的水果识别检测系统&#xff0c;可识别图片和视频当中出现的六类常见的水果&#xff0c;包括&#xf…

【redis】非关系型数据库——Redis介绍与安装(windows环境)

目录 数据库架构的演化单体架构缓存(Memcached)MySQL集群缓存(Memcached可以)MySQL集群垂直拆分&#xff08;主从复制&#xff0c;读写分离&#xff09;缓存(Redis)MySQL集群垂直拆分分库分表 NoSQLNoSQL产生的背景性能需求MySQL的扩展性瓶颈方面什么是NoSQLNoSQL的特点主流的N…

下级平台级联EasyCVR视频汇聚安防监控平台后,设备显示层级并存在重复的原因排查和解决

视频汇聚平台/视频监控系统/国标GB28181协议EasyCVR安防平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;平台支持7*24小时实时高清视频监控&#xff0c;能同时…

C语言进阶|单链表的实现

✈链表的概念和结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表 中的指针链接次序实现的。 链表的结构跟火车车厢相似&#xff0c;淡季时车次的车厢会相应减少&#xff0c;旺季时车次的车厢会额外增加几节。…

BSV区块链协会上线首个版本的ARC交易处理器

​​发表时间&#xff1a;2024年3月28日 BSV区块链协会近期上线了首个版本的ARC交易处理器。ARC是一项区块链交易处理服务&#xff0c;能在通过P2P网络广播交易之前验证并存储相关的交易。一旦新区块被挖出&#xff0c;一条与该交易相关的Merkle路径将被发回给交易发起者作为确…

长效静态代理IP如何改变你的SEO和网络营销策略?

长效静态代理IP为SEO和网络营销专家提供了一个强大的工具&#xff0c;通过这种技术&#xff0c;可以突破传统的限制&#xff0c;以全新的视角和方法优化其在线策略。这不仅增强了企业的市场竞争力&#xff0c;也为实现更高效、更精准的营销目标提供了可能。 一、长效静态代理IP…

Vue面试经验

Vue编译时声明周期的执行顺序 Vue中父子组件渲染顺序&#xff08;同步引入子组件&#xff1a;import Son from ‘/components/son’ &#xff09; 父子组件编译时的生命周期执行顺序 这里修改data数据时也修改了dom&#xff0c;如过知识通过按钮对数据进行操作&#xff0c;那…

kafka大数据采集技术实验(未完待续)

Kafka环境搭建 下载地址&#xff1a;https://link.zhihu.com/?targethttps%3A//kafka.apache.org/downloads解压启动zookeeper bin/zookeeper-server-start.sh config/zookeeper.properties需要注意的是 : " c o n f i g / z o o k e e p e r . p r o p e r t i e s &q…

解密Java线程池源码

一、线程池中的保活和回收源码分析 1、线程池中线程的创建时机 1、核心线程创建时机 在研究线程池的源码前首先想一个问题 public class Main {public static void main(String[] args) {ThreadPoolExecutor executor new ThreadPoolExecutor(10, 20, 0l, TimeUnit.MILLIS…

从Linux角度具体理解程序翻译过程-----预处理、编译、汇编、链接

目录 前言&#xff1a; 翻译过程 1.预处理 2.编译 3.汇编 4.链接 Linux下对其理解&#xff1a; 1.预处理 拓展&#xff1a; Linux下文件信息&#xff1a; 文件类型&#xff1a; 硬链接数&#xff1a; 文件拥有者&#xff1a; 文件所属组&#xff1a; other&#x…

区块链安全应用-------压力测试

基于已有的链进行测试&#xff08;build_chain默认建的链 四个节 点&#xff09;&#xff1a; 第一步&#xff1a;搭链 1. 安装依赖 在ubuntu操作系统中&#xff0c;操作步骤如下&#xff1a; sudo apt install -y openssl curl 2. 创建操作目录, 下载安装脚本 ## 创建操作…

3个比较不错的Linux云音乐应用程序整理

在现代音乐流媒体时代&#xff0c;基于云的音乐应用程序因其便利性和可访问性而变得非常流行。Linux 用户尤其寻求可靠且功能丰富的音乐播放器来无缝地享受他们喜爱的音乐。 在这里&#xff0c;我们探讨了三个最好的基于云的音乐应用程序&#xff0c;每个应用程序都提供专为 L…

Java Web 网页设计(1)

不要让追求之舟停泊在幻想的港湾 而应扬起奋斗的风帆 驶向现实生活的大海 网页设计 1.首先 添加框架支持 找到目录右键添加 找到Web Application选中 点击OK 然后 编辑设置 找到Tomcat--local 选中 点击OK 名称可以自己设置 找到对应文件夹路径 把Tomcat添加到项目里面 因为…

【Hadoop】-HDFS的Shell操作[3]

目录 前言 一、HDFS集群启停命令 1.一键启停脚本可用 2.独立进程启停可用 二、文件系统操作命令 1、创建文件夹 2、查看指定目录下内容 3、上传文件到HDFS指定目录下 4、查看HDFS文件内容 5、下载HDFS文件 6、拷贝HDFS文件 7、追加数据到HDFS文件中 8、HDFS数据移…

哪吒汽车把最后的翻身筹码,全压在了这辆新车上

正如比亚迪王传福所说&#xff0c;新能源车市场已进入惨烈淘汰赛环节。 近几年国内新能源车销量增长势头迅猛&#xff0c;仅过去的 2023 年产销便分别达 958.7 万辆和 949.5 万辆&#xff0c;同比增长 35.8% 和 37.9%。 销量高速增长背后自然也带来了越来越激烈的竞争。 过去…

Footprint Analytics 与 GalaChain 达成战略合作

​ Footprint Analytics 宣布与 GalaChain 达成战略合作。GalaChain 是 Gala 旗下的 Layer 1 区块链。此次合作标志着双方在游戏&#xff08;包括 Gala Games) 、娱乐和金融等多个行业的区块链生态系统革新方面迈出了重要的一步。 GalaChain 致力于满足企业级项目的广泛需求&…

算法-栈操作

1047. 删除字符串中的所有相邻重复项 - 力扣&#xff08;LeetCode&#xff09; class Solution { public:string removeDuplicates(string s) {string stack;for(char& ch:s){if(stack.size()>0&&chstack.back()){stack.pop_back();}else{stack.push_back(ch);}…

AI大模型实现软件智能化落地实践

1、什么是大模型 大型语言模型&#xff08;Large Language Model&#xff0c;LLM&#xff1b;Large Language Models&#xff0c;LLMs)。 大语言模型是一种深度学习模型&#xff0c;特别是属于自然语言处理&#xff08;NLP&#xff09;的领域&#xff0c;一般是指包含数干亿&…

Pandas 模块-操纵数据(11)-二元运算--超级add、sub、mul、div、mod、pow等等

目录 1. DataFrame.add 1.1 DataFrame.add 语法结构 1.2 DataFrame.add 参数说明 1.3 DataFrame.add 用法示例 1.3.1 正常的使用 1.3.2 需要注意类型相符合 2. DataFrame.sub 2.1 DataFrame.sub 语法结构 2.2 DataFrame.sub 参数说明 2.3 DataFrame.sub 用法示例 3.…