【Spring全家桶系列】AspectJ表达式的书写与SpringAOP下的五种通知类型

news2025/1/20 7:23:07

⭐️前面的话⭐️

本文已经收录到《Spring框架全家桶系列》专栏,本文将介绍AspectJ表达式的书写与SpringAOP下的五种通知类型。

📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2023年5月15日🌴
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《无》
💬参考在线编程网站:🌐牛客网🌐力扣🌐acwing
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!


📌导航小助手📌

    • 1.AspectJ表达式
      • 1.1语法格式与含义
      • 1.2通配符
      • 1.3注意事项
      • 1.4扩展知识
    • 2.Spring中五种通知模式
      • 2.1运行前通知@Before
      • 2.2运行后通知@After
      • 2.3返回后通知@AfterReturning
      • 2.4抛异常通知@AfterThrowing
      • 2.5环绕通知@Around
    • 3.通知参数的获取
      • 3.1通知方法获取参数列表
      • 3.2通知方法返回返回值
      • 3.3通知方法获取异常
    • 4.通知类型总结
      • 知识点1:@Before
      • 知识点2:@After
      • 知识点3:@AfterReturning
      • 知识点4:@AfterThrowing
      • 知识点5:@Around


封面


1.AspectJ表达式

1.1语法格式与含义

AspectJ表达式是一种切点匹配表达式,用来匹配切点对应的方法,它的语法格式如下:

execution(访问权限修饰符(可省) 返回值 包路径.类名.方法名(形参类型表) 异常(可省略))

具体说明:

  • 访问权限修饰符,表示所匹配方法的访问权限,可省略,省略表示访问权限不限,在实际开发中基本上都是对public修饰的方法做拦截或者增强,此项一般可以不写。
  • 返回值,必填项,表示所匹配方法的返回值类型。
  • 包路径,必填项,表示在哪个或者哪些包下进行查找方法。
  • 类名称,必填项,表示在哪个类或者哪些类下进行匹配方法。
  • 方法名,必填项,表示匹配的方法名是什么。
  • 异常,可选项,表示匹配可能会抛出对应类型的方法,一般省略,异常有其他方式做统一拦截处理。

下面来做一些小练习:

execution(void com.aop.demo.controller.AddController.playing())

表示匹配任意权限返回值为void,处于com.aop.demo.controller.AddController类下的无参名为playing的方法。

execution(void com.aop.demo.controller.AddController.playing(int, String))

表示匹配任意权限返回值为void,处于com.aop.demo.controller.AddController类下的有两个参数并且参数列表分别为int String并且名为playing的方法。

1.2通配符

AspectJ表达式支持三种通配符:

  • *,匹配任意字符,但只能匹配一个元素。
  • ..,匹配任意字符,可以匹配多个元素 ,在表示类时,必须和 * 联合使用。
  • +,表示按照类型匹配指定类的所有类,包括匹配类本身,还包括继承该类的所有子类,必须跟在类名后面,如 com.cad.Car+ ,表示继承该类的所有子类(包括本身)。

来做几个小练习:

execution(void com.aop.demo.controller.*Controller.*ing(*,*))

表示所有权限返回值为void,在com.aop.demo.controller包下所有以Controller结尾的类或接口下含有两个参数并方法名以ing结尾的所有方法。

execution(void com..controller.*Controller.*ing(..))

表示所有权限返回值为void,在com包已经子包下所有以Controller结尾的类或接口下含有任意参数列表类型并方法名以ing结尾的所有方法。

execution(void com.*.*.*.*.update())

表示所有权限返回值为void,在com包下任意三级包下的任意类(或接口)中无参数名为update所有方法。

execution(void *..update())

表示所有权限返回值为void,在com包下任意包中的任意类(或接口)中无参数名为update所有方法。

execution(* *..*Service+.*(..))

表示任意权限返回任意类型返回值,在任何包以及子包下以Service结尾的类(或接口)及其继承(实现)该类(接口)的所有子类(子接口)下的任意参数列表类型任意方法名的所有方法。

这样写其实就是包含了与Service类及其相关类的所有方法了,不推荐这么写,因为搜索也是需要成本的,还是你需要匹配什么你就具体写出来。

1.3注意事项

对于切入点表达式的编写其实是很灵活的,要注意以下几点:

  • 所有代码按照标准规范开发,否则后续所有注意全部失效。
  • 描述切入点通常描述接口,而不描述实现类,如果描述到实现类,就出现紧耦合了。
  • 访问控制修饰符针对接口开发均采用public描述(可省略访问控制修饰符描述)。
  • 返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述。
  • 包名书写尽量不使用…匹配,效率过低,常用*做单个包描述匹配,或精准匹配。
  • 接口名/类名书写名称与模块相关的采用*匹配,例如UserService书写成*Service,绑定业务层接口名。
  • 方法名书写以动词进行准匹配,名词采用*匹配,例如getById书写成getBy*,selectAll书写成selectAll
  • 参数规则较为复杂,根据业务方法灵活调整。
  • 通常不使用异常作为匹配规则。

1.4扩展知识

切入点指示符用来指示切入点表达式目的,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:

         execution:用于匹配方法执行的连接点;

         within:用于匹配指定类型内的方法执行;

         this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也类型匹配;

         target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配;

         args:用于匹配当前执行的方法传入的参数为指定类型的执行方法;

         @within:用于匹配所以持有指定注解类型内的方法;

         @target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解;

         @args:用于匹配当前执行的方法传入的参数持有指定注解的执行;

         @annotation:用于匹配当前执行方法持有指定注解的方法;

         bean:Spring AOP扩展的,AspectJ没有对于指示符,用于匹配特定名称的Bean对象的执行方法;

         reference pointcut:表示引用其他命名切入点,只有@ApectJ风格支持,Schema风格不支持。

AspectJ切入点支持的切入点指示符还有: callgetsetpreinitializationstaticinitializationinitializationhandleradviceexecutionwithincodecflowcflowbelowif@this@withincode;但Spring AOP目前不支持这些指示符,使用这些指示符将抛出IllegalArgumentException异常,这些指示符Spring AOP可能会在以后进行扩展。

2.Spring中五种通知模式

首先来说明一下演示项目的结构:

1

Spring配置的实现和依赖和上一节AOP实现步骤案例是一样的。

TestServiceImpl与TestService:

public interface TestService {
    public void testBefore();
    public void testAfter();
    public void testAround();
    public void testAfterThrowing();
    public void testAfterReturning();
    public String testHaveReturnAround();
    public void test(int a, String s);
    public String test();
}
@Service
public class TestServiceImpl implements TestService {
    @Override
    public void testBefore() {
        System.out.println("业务执行中...");
    }
    @Override
    public void testAfter() {
        System.out.println("业务执行中...");
    }
    @Override
    public void testAround() {
        System.out.println("业务执行中...");
    }
    @Override
    public String testHaveReturnAround() {
        System.out.println("业务执行中...");
        return "我有返回值!";
    }
    @Override
    public void testAfterThrowing() {
        System.out.println("业务执行中...");
        try {
            int a = 1 / 0;
        } catch (ArithmeticException e) {
            System.out.println("存在除0异常!");
            e.printStackTrace();
        }
    }
    @Override
    public void testAfterReturning() {
        System.out.println("业务执行中...");
    }

    @Override
    public void test(int a, String s) {
        System.out.println("含参目标方法!");
    }

    @Override
    public String test() {
        System.out.println("含返回值目标方法!");
        return "test返回值!";
    }
}

2.1运行前通知@Before

运行前通知就是在执行目标方法前,执行通知方法,用法很简单,创建好SpringAOP环境后,我们只需在通知类中做如下步骤:

第一步,设置切点,就是使用@Pointcut注解,参数为AspectJ表达式,匹配方法。

2

第二步,设置通知,执行前通知使用@Before注解,参数为切点的方法名以及参数列表。
第三步,实现通知。

3

测试代码:

4
运行结果:
5

2.2运行后通知@After

意思就是在目标方法执行完毕后执行运行后通知方法。

运行后通知设置步骤后运行前设置步骤一模一样,就将@Before注解改为@After即可,其他的切点配置和切面配置对应着改一改即可。

切点:
6
切面和通知:

7
验证代码:

8

运行结果:

9

2.3返回后通知@AfterReturning

意思是在目标方法返回后,执行返回后通知方法,如果发生异常则该类型的通知方法不执行,做法是一样的,就不细说了。

切点:
10

切面和通知:
11

验证程序:

12
运行结果:

13

2.4抛异常通知@AfterThrowing

抛异常通知指的就是如果目标方法执行出现异常,则执行抛异常通知否则不执行,为了验证它真的在抛异常后执行了,我们在service实现类中故意让目标方法抛出异常。
111

用法同上。

切点:
16

切面与通知:
15
验证程序:

16
运行结果:

17

2.5环绕通知@Around

环绕通知就是在目标方法执行前可以执行环绕通知一部分逻辑,目标方法执行后执行环绕通知一部分逻辑,这个实现就有一点不一样了,因为你写一个环绕通知方法,里面的代码编译器或者jvm或者spring也不知道你那些代码在目标方法之前执行,哪些方法在目标方法之后执行。

要解决这个问题,得将目标方法抽提出来,然后将目标方法执行时机放入环绕通知合适的位置才可以解决,SpringAOP就帮助我们做了这件事情,在环绕通知方法参数中,可以加上一个ProceedingJoinPoint类型的参数,带类型的对象出了可以调用切点方法,还可以获取具体执行方法的信息。

调用 proceed方法就可以调用目标方法,调用getSignature方法就能获得目标方法的详细信息,包括所在包,所在类,以及具体方法名等,当这个对象不仅仅可以获取这些内容,还有很多也能获取其他的就自己去探索吧。

首先配置切点:

18
配置切面和通知:

19
验证程序:

20
运行结果:
21
这样写当然是没有问题的,因为它运行都符合预期了,但是事实上实现通知的方法更推荐带一个返回值毕竟只是刚好我们执行的这个方法没有返回值罢了,如果一个方法实际有返回值,但是你在实现通知类的实例没有将返回值返回出去那么在验证的时候,它的返回值会是null

假设通知类实现不变,我们换一个有返回值的目标方法,输出它返回的结果,验证我们的猜想是否正确。
22

验证程序:

23
运行结果:

24

最终返回的结果是null,那如何将返回值带出去呢?考虑到通知方法可能会用于很多目标方法,因此我们给通知方法进行Object对象的返回,我们在实现时,直接将proceed方法执行的返回值保存再返回,就是原目标方法返回的内容了。

25
验证程序不变,运行结果如下:

26

当然,AOP通知是能够篡改原目标方法的返回值的,这样在原目标方法执行出现我们意料之外或错误的情况时,我们可以通过AOP进行纠正。

如何篡改呢?就是将通知方法返回值改变即可,假设变为执行方法的信息:

27
验证程序不变,运行结果如下:

28

3.通知参数的获取

3.1通知方法获取参数列表

对于通知,其实是可以获取到目标方法的一些信息的,不仅仅只有环绕通知能够获取,非环绕通知也是能够获取的。

对于非环绕通知可以在通知方法参数列表加上JoinPoint类型的参数,使用Object数组就能获取目标方法的参数了。

目标方法如下:

29

AOP实现:

30

测试程序:
31

运行结果:
32
对于环绕通知也是一样的,不过使用的是我们熟悉的ProceedingJoinPoint类型的参数,其实就是JoinPoint的子类了,为什么不使用JoinPoint呢,因为JoinPoint没有proceed方法,那为什么非环绕通知不使用ProceedingJoinPoint类型的参数呢,因为Spring不允许在非环绕通知类使用ProceedingJoinPoint类型的参数,两者的使用方法基本上一模一样。

33
运行结果:

34

3.2通知方法返回返回值

对于返回值,只有返回后AfterReturing和环绕Around这两个通知类型可以获取。

环绕通知前面已经说明过了,直接使用Object对象获取原目标方法执行结果即可,并且可以篡改。

AfterReturing通知获取方式如下,在通知方法加上一个Object类型参数,名字假设为ret,再设置@AfterReturing注解中的returning参数为ret,即与参数同名,即可获取参数。

目标方法如下:
35
AOP实现如下:
36

验证程序:

37

运行结果:
38

3.3通知方法获取异常

对于环绕方法就简单了,因为它需要抛目标方法可能会产生的异常,我们直接能捕获异常即可。对于其他非环绕通知,只有抛异常后通知可以获取异常,获取方式与返回后通知获取返回值比较像。

即给通知加上Throwable类型参数t,并在@AfterThrowing的throwing参数写同名参数t,然后这个t变量就是获取到的异常。

因为与返回后通知获取返回值比较像,我就不写具体案例了,小伙伴可以基于我的案例进行补充。

我直接在前面演示抛异常的例子上进行补充,目标方法:
39-0

AOP实现:
39
40
运行结果:

41
至此,AOP通知如何获取数据就已经讲解完了,数据中包含参数返回值异常(了解)

4.通知类型总结

知识点1:@Before

名称@Before
类型方法注解
位置通知方法定义上方
作用设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行

知识点2:@After

名称@After
类型方法注解
位置通知方法定义上方
作用设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行

知识点3:@AfterReturning

名称@AfterReturning
类型方法注解
位置通知方法定义上方
作用设置当前通知方法与切入点之间绑定关系,当前通知方法在原始切入点方法正常执行完毕后执行

知识点4:@AfterThrowing

名称@AfterThrowing
类型方法注解
位置通知方法定义上方
作用设置当前通知方法与切入点之间绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行

知识点5:@Around

名称@Around
类型方法注解
位置通知方法定义上方
作用设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行

觉得文章写得不错的老铁们,点赞评论关注走一波!谢谢啦!

1-99

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

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

相关文章

基于ArcGIS实现陕西省1:250000比例尺地形图分幅和编号

1地形图的分幅与编号原理 我国1:1000000地形图的分幅采用国际1:1000000地图分幅标准,而其他比例尺地形图分幅以1:1000000比例尺地形图为基准进行分幅。每幅1:1000000地形图范围是经差6、纬差4;纬度60~ 76之间经差12、纬差4;纬度76~ 88之间经…

数据结构之数组对栈的实现

目录 1.什么是栈 2.栈的基本操作 3.栈的实现 1.结构体与函数接口 2.初始化栈 3.销毁栈 4.入栈 5.出栈 6.返回栈元素数量大小 7.判空 8.打印栈 1.什么是栈 栈是一种特殊的线性表,其中只允许固定的一端进行插入与删除,进行输出插入删除的那一端我…

振幅调制器【Multisim】【高频电子线路】

目录 一、实验目的与要求 二、实验仪器 三、实验内容与测试结果 1、观测集电极调幅器输出信号波形,测量调幅度 2、观察集电极调幅器输出信号频谱(Fourier analysis) 3、改变V1幅度为0.8Vpk,观测输出波形,说明原…

MySQL函数详细

♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️树高千尺,落叶归根人生不易&…

设计模式之【享元模式】,共享单车火起来并不是没有原因的

文章目录 一、什么是享元模式1、享元模式的角色2、享元模式应用场景3、享元模式的内部状态和外部状态4、享元模式的优缺点5、享元模式跟单例的区别6、享元模式跟缓存的区别7、享元模式注意事项及细节 二、实例1、享元模式一般写法2、俄罗斯方块案例3、购票业务案例4、数据库连接…

win2012/win2016/win2019 IIS部署SSL证书访问https(支持多站点)

请根据操作系统、站点部署数量选择以下相应参考文档,文档仅供参考。 A、windows2008iis7环境SSL部署https单/多站点 B、linux系统SSL部署https单/多站点 C、windows2003系统SSL单站点部署https 部署https(ssl)后设置301跳转将http跳转到https 亚数机房香港IP部…

(2022 EMNLP)(SEMGraph)将情感知识和眼动结合到一个图架构中

论文题目(Title):SEMGraph: Incorporating Sentiment Knowledge and Eye Movement into Graph Model for Sentiment Analysis 研究问题(Question):基于眼动的情感分析,旨在绘制基于眼动的情感关…

【Java多线程编程】解决线程的不安全问题之synchronized关键字

前言: 当我们进行多线程编程时候,多个线程抢占系统资源就会造成程序运行后达不到想要的需求。我们可以通过 synchronized 关键字对某个代码块或操作进行加锁。这样就能达到多个线程安全的执行,因此我把如何使用 synchronized 进行加锁的操作…

PVE安装高大全固件

PVE安装高大全的时候,把IMG挂在光驱上,发现无法运行。 后来明白了,openwrt的镜像直接就是个系统磁盘镜像, 没有引导启动项和安装程序,度娘和CSDN后才知道。 ———— 创建个虚拟机,按照引导创建虚拟机即…

女网红靠GPT-4交1000+男友,聊天按分钟收费,一周收入50万

来源 | 量子位 作者 | 鱼羊 注意看,这个女人叫卡琳,靠着GPT-4,她现在同时谈着1000男朋友。 对,我知道事情听上去有些离谱。就连GPT-4自己,都直呼“我一个AI都觉得非常不常见”。 但是先别急,因为更让人挠头…

一个用于Allen脑图谱基因数据的工具箱|abagen详细使用教程-获取基于脑区的基因表达矩阵(脑区*gene)

艾伦人类脑图谱(Allen Human Brain Atlas) 艾伦人类脑图谱是一个由艾伦脑科学研究所(Allen Institute for Brain Science)开发的在线基因表达图谱数据库,旨在提供人类大脑各个区域的细胞类型和基因表达信息。这个数据库包含了人类全基因组微…

工业识别与定位系统源码解决方案

工厂人员定位系统源码,工业领域定位系统源码 近年来人员定位系统在工业领域的发展势头迅猛,工业识别与定位成为促进制造业数字化的关键技术。通过实时定位可以判断所有的人、物、车的位置。实时定位系统要适用于复杂工业环境,单一技术是很难…

tinyWebServer 学习笔记——二、HTTP 连接处理

文章目录 一、基础知识1. epoll2. 再谈 I/O 复用3. 触发模式和 EPOLLONESHOT4. HTTP 报文5. HTTP 状态码6. 有限状态机7. 主从状态机8. HTTP_CODE9. HTTP 处理流程 二、代码解析1. HTTP 类2. 读取客户数据2. epoll 事件相关3. 接收 HTTP 请求4. HTTP 报文解析5. HTTP 请求响应 …

OpenText 数据迁移解决方案的工作原理及其优势

OpenText Migrate 让迁移变得简单 选择正确的迁移技术您所需要了解的事情 无痛迁移 当谈到停机的常见原因时,灾难往往会得到最多的关注。 但灾难只是导致停机的一小部分原因。 计划的停机时间造成了资源的真正消耗,许多纯粹为灾难恢复应运而生的工具和…

【Simulink】 0基础入门教程 P2 常用模块的使用介绍

目录 常用模块介绍 (1) relational operator,用于数值的大小比较 (2) compare to constant,用于和数值做大小比较 (3)logical operator,用于逻辑运算 与运算 或运算 非运算 (4) switch,类似于C语言中的if 语句&#xff0c…

stm32 MCU液晶TM1622 HT1622驱动调试

本文使用的例程软件工程代码如下 (1条消息) stm32MCU液晶TM1622HT1622驱动调试,源代码,实际项目使用资源-CSDN文库 HT1622/HT1622G/TM1622是一款常用的LCD驱动芯片 TM1622/HT1622厂家不一样,但是芯片功能基本上一直,硬件上基本…

『C++』C++的类型转换

「前言」文章是关于C特殊类型转换 「归属专栏」C嘎嘎 「笔者」枫叶先生(fy) 「座右铭」前行路上修真我 「枫叶先生有点文青病」 「每篇一句」 有些事不是看到了希望才去坚持, 而是因为坚持才会看到希望。 ——《十宗罪》 目录 一、C语言中的类型转换 二、为什么C需…

tinyWebServer 学习笔记——三、定时器处理非活跃链接

文章目录 一、基础知识1. 概念2. API3. 信号处理机制 二、代码解析1. 信号处理函数2. 信号通知逻辑3. 定时器4. 定时器容器5. 定时任务处理函数6. 使用定时器 参考文献 一、基础知识 1. 概念 非活跃:指客户端与服务器建立连接后,长时间不交换数据&…

第二章 数据的表示和运算

1.进位计数制 其他进制转十进制 二进制<——> 八进制&#xff0c;十六进制 (注意&#xff1a;小数部分也是从右往左算十进制——>任意进制&#xff08;整数部分&#xff09; 十进制——>任意进制&#xff08;小数部分&#xff09; 十进制转二进制&#xff08;拼凑…

【gitee流水线实现自动化部署】

首先进入自己的gitee仓库 创建流水线 配置基本信息 名称标识 事件监听 -----触发条件 主要是任务排编内 vue前端则选择node构建 这些就是字面意思 若无特殊需求 按照默认的即可 构建完之后添加新任务 主机部署 选择部署 主机部署 添加主机组 新建主机组 自主导入 之后配…