Spring中AOP的通知类型和执行顺序

news2025/4/7 17:54:19

Spring中AOP的通知类型:

  • @Around:环绕通知,此注解标注的通知方法在目标方法前、后都被执行
  • @Before:前置通知,此注解标注的通知方法在目标方法前被执行
  • @After :后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
  • @AfterReturning : 返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行
  • @AfterThrowing : 异常后通知,此注解标注的通知方法发生异常后执行
package com.mkyuan.aop;

import lombok.extern.slf4j.Slf4j;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @Author sygui
 * @Date 2023-07-23
 **/
@Component
@Slf4j
@Aspect
public class TimeAspect {
    //前置通知
    @Before("execution(* com.mkyuan.service.*.*(..))")
    public void before(JoinPoint joinPoint) {
        log.info("Before...");
    }

    //环绕通知
    @Around("execution(* com.mkyuan.service.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Around before...");
        //调用目标对象的原始方法执行
        Object proceed = joinPoint.proceed();
        //原始方法如果执行时有异常,环绕通知中的后置代码不会在执行了
        log.info("Around after..");
        return proceed;

    }

    //返回后通知(程序在正常执行的情况下,会执行的后置通知)
    @AfterReturning("execution(* com.mkyuan.service.*.*(..))")
    public void afterReturn(JoinPoint joinPoint) {
        log.info("AfterReturning...");

    }

    //异常通知(程序在出现异常的情况下,执行的后置通知)
    @AfterThrowing("execution(* com.mkyuan.service.*.*(..))")
    public void afterThrow(JoinPoint joinPoint) {
        log.info("AfterThrowing...");

    }

    //后置通知
    @After("execution(* com.mkyuan.service.*.*(..))")
    public void after(JoinPoint joinPoint) {
        log.info("After...");
    }

}

1. 没有异常情况下:

程序没有发生异常的情况下,@AfterThrowing标识的通知方法不会执行。

2. 出现异常情况下:

修改EmpServiceImpl业务实现类中的代码: 添加异常

 /**
     * 根据id查询员工
     *
     * @param id
     * @return
     */
    @Override
    public Emp selectEmp(Integer id) {
        empMapper.selectEmpById(id);
        int i = 1 / 0;
        return empMapper.selectEmpById(id);
    }

程序发生异常的情况下:

  • @AfterReturning标识的通知方法不会执行,@AfterThrowing标识的通知方法执行了

  • @Around环绕通知中原始方法调用时有异常,通知中的环绕后的代码逻辑也不会在执行了 (因为原始方法调用已经出异常了)

优化切面类

Spring提供了@PointCut注解,该注解的作用是将公共的切入点表达式抽取出来,需要用到时引用该切入点表达式即可。

@Component
@Slf4j
@Aspect
public class TimeAspect {
    @Pointcut("execution(* com.mkyuan.service.*.*(..))")
    private void pt() {

    }

    //前置通知
    @Before("pt()")
    public void before(JoinPoint joinPoint) {
        log.info("Before...");
    }

    //环绕通知
    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("Around before...");
        //调用目标对象的原始方法执行
        Object proceed = joinPoint.proceed();
        //原始方法如果执行时有异常,环绕通知中的后置代码不会在执行了
        log.info("Around after..");
        return proceed;
    }

    //返回后通知(程序在正常执行的情况下,会执行的后置通知)
    @AfterReturning("pt()")
    public void afterReturn(JoinPoint joinPoint) {
        log.info("AfterReturning...");
    }

    //异常通知(程序在出现异常的情况下,执行的后置通知)
    @AfterThrowing("pt()")
    public void afterThrow(JoinPoint joinPoint) {
        log.info("AfterThrowing...");
    }

    //后置通知
    @After("pt()")
    public void after(JoinPoint joinPoint) {
        log.info("After...");
    }
}

需要注意的是:当切入点方法使用private修饰时,仅能在当前切面类中引用该表达式, 当外部其他切面类中也要引用当前类中的切入点表达式,就需要把private改为public,而在引用的时候,具体的语法为:

@Component
@Slf4j
@Aspect
public class TimeAspect1 {
    @Before("com.mkyuan.aop.TimeAspect.pt()")
    public void before() {
        log.info("TimeAspect -> before ...");
    }
}

@annotation

已经学习了execution切入点表达式的语法。那么如果我们要匹配多个无规则的方法,比如:list()和 delete()这两个方法。这个时候我们基于execution这种切入点表达式来描述就不是很方便了。而在之前我们是将两个切入点表达式组合在了一起完成的需求,这个是比较繁琐的。

我们可以借助于另一种切入点表达式annotation来描述这一类的切入点,从而来简化切入点表达式的书写。

实现步骤:

  1. 编写自定义注解

  2. 在业务类要做为连接点的方法上添加自定义注解

自定义注解:MyLog

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}

业务类:DeptServiceImpl

@Slf4j
@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptMapper deptMapper;

    @Override
    @MyLog //自定义注解(表示:当前方法属于目标方法)
    public List<Dept> list() {
        List<Dept> deptList = deptMapper.list();
        //模拟异常
        //int num = 10/0;
        return deptList;
    }

    @Override
    @MyLog  //自定义注解(表示:当前方法属于目标方法)
    public void delete(Integer id) {
        //1. 删除部门
        deptMapper.delete(id);
    }


    @Override
    public void save(Dept dept) {
        dept.setCreateTime(LocalDateTime.now());
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.save(dept);
    }

    @Override
    public Dept getById(Integer id) {
        return deptMapper.getById(id);
    }

    @Override
    public void update(Dept dept) {
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.update(dept);
    }
}

切面类

@Component
@Slf4j
@Aspect
public class MyAspect {
    //针对list方法、delete方法进行前置通知和后置通知

    //前置通知
    @Before("@annotation(com.mkyuan.anno.MyLog)")
    public void before(){
        log.info("MyAspect  -> before ...");
    }

    //后置通知
    @After("@annotation(com.mkyuan.anno.MyLog)")
    public void after(){
        log.info("MyAspect  -> after ...");
    }
}

到此我们两种常见的切入点表达式我已经介绍完了。

  • execution切入点表达式
    • 根据我们所指定的方法的描述信息来匹配切入点方法,这种方式也是最为常用的一种方式
    • 如果我们要匹配的切入点方法的方法名不规则,或者有一些比较特殊的需求,通过execution切入点表达式描述比较繁琐
  • annotation 切入点表达式
    • 基于注解的方式来匹配切入点方法。这种方式虽然多一步操作,我们需要自定义一个注解,但是相对来比较灵活。我们需要匹配哪个方法,就在方法上加上对应的注解就可以了

AOP通知的执行顺序

当在项目开发当中,我们定义了多个切面类,而多个切面类中多个切入点都匹配到了同一个目标方法。此时当目标方法在运行的时候,这多个切面类当中的这些通知方法都会运行。

此时我们就有一个疑问,这多个通知方法到底哪个先运行,哪个后运行? 下面我们通过程序来验证(这里呢,我们就定义两种类型的通知进行测试,一种是前置通知@Before,一种是后置通知@After)

定义多个切面类:

MyAspect1

@Component
@Slf4j
@Aspect
public class MyAspect1 {
    @Before("execution(* com.mkyuan.service.*.*(..))")
    public void before(){
        log.info("MyAspect1 -> before ...");
    }

    //后置通知
    @After("execution(* com.mkyuan.service.*.*(..))")
    public void after(){
        log.info("MyAspect1 -> after ...");
    }
}

MyAspect2

@Component
@Slf4j
@Aspect
public class MyAspect2 {
    @Before("execution(* com.mkyuan.service.*.*(..))")
    public void before(){
        log.info("MyAspect2 -> before ...");
    }

    //后置通知
    @After("execution(* com.mkyuan.service.*.*(..))")
    public void after(){
        log.info("MyAspect2 -> after ...");
    }
}

MyAspect3

@Component
@Slf4j
@Aspect
public class MyAspect3 {
    @Before("execution(* com.mkyuan.service.*.*(..))")
    public void before(){
        log.info("MyAspect3 -> before ...");
    }

    //后置通知
    @After("execution(* com.mkyuan.service.*.*(..))")
    public void after(){
        log.info("MyAspect3 -> after ...");
    }
}

通过以上程序运行可以看出在不同切面类中,默认按照切面类的类名字母排序:

  • 目标方法前的通知方法:字母排名靠前的先执行

  • 目标方法后的通知方法:字母排名靠前的后执行

如果我们想控制通知的执行顺序有两种方式:

  1. 修改切面类的类名(这种方式非常繁琐、而且不便管理)

  2. 使用Spring提供的@Order注解

使用@Order注解,控制通知的执行顺序:

MyAspect1

@Component
@Slf4j
@Aspect
@Order(3)
public class MyAspect1 {
    @Before("execution(* com.mkyuan.service.*.*(..))")
    public void before(){
        log.info("MyAspect1 -> before ...");
    }

    //后置通知
    @After("execution(* com.mkyuan.service.*.*(..))")
    public void after(){
        log.info("MyAspect1 -> after ...");
    }
}

MyAspect2

@Component
@Slf4j
@Aspect
@Order(2)
public class MyAspect2 {
    @Before("execution(* com.mkyuan.service.*.*(..))")
    public void before(){
        log.info("MyAspect2 -> before ...");
    }

    //后置通知
    @After("execution(* com.mkyuan.service.*.*(..))")
    public void after(){
        log.info("MyAspect2 -> after ...");
    }
}

MyAspect3

@Component
@Slf4j
@Aspect
@Order(1)
public class MyAspect3 {
    @Before("execution(* com.mkyuan.service.*.*(..))")
    public void before(){
        log.info("MyAspect3 -> before ...");
    }

    //后置通知
    @After("execution(* com.mkyuan.service.*.*(..))")
    public void after(){
        log.info("MyAspect3 -> after ...");
    }
}

通知的执行顺序大家主要知道两点即可:

  1. 不同的切面类当中,默认情况下通知的执行顺序是与切面类的类名字母排序是有关系的
  2. 可以在切面类上面加上@Order注解,来控制不同的切面类通知的执行顺序

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

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

相关文章

Jmeter+Jenkins+Ant自动化持续集成环境搭建

一、安装准备 1.JDK:jdk-8u121-windows-x64 2.jmeter工具&#xff1a;apache-jmeter-2.13 3.ANT工具&#xff1a;apache-ant-1.9.7-bin 4.jenkins工具&#xff1a;jenkins-2.32.2 二、软件安装 1.JDK的安装 >双击JDK安装包&#xff0c;选择安装路径&#xff08;本人是…

论文笔记--ERNIE: Enhanced Language Representation with Informative Entities

论文笔记--ERNIE: Enhanced Language Representation with Informative Entities 1. 文章简介2. 文章概括3 文章重点技术3.1 模型框架3.2 K-Encoder(Knowledgeable Encoder)3.3 预训练任务3.4 微调 4. 文章亮点5. 原文传送门6. References 1. 文章简介 标题&#xff1a;ERNIE:…

每天五分钟计算机视觉:单卷积层的前向传播过程

什么是单卷积层? 一张图片(输入)经过多个卷积核卷积就会得到一个输出,而这多个卷积核的组合就是一个单卷积层。 这些卷积核可能大小是不一样的,但是他们接收同样大小是输入,他们的输出必须是一般大小,所以不同的卷积核需要具备不同的步长和填充值。 单层卷积网络前向传…

springboot+vue开发后台增删改查

效果图 前端代码【User.vue】 <template><div class"data-container"><!--添加 start--><div class"data-header"><el-button round click"addHander" size"large" type"primary"><el-ic…

Cesium态势标绘专题-普通点(标绘+编辑)

标绘专题介绍:态势标绘专题介绍_总要学点什么的博客-CSDN博客 入口文件:Cesium态势标绘专题-入口_总要学点什么的博客-CSDN博客 辅助文件:Cesium态势标绘专题-辅助文件_总要学点什么的博客-CSDN博客 本专题没有废话,只有代码,代码中涉及到的引入文件方法,从上面三个链…

ORB-SLAM2学习笔记5之EuRoc、TUM和KITTI开源数据运行ROS版ORB-SLAM2生成轨迹

文章目录 0 引言1 数据预处理1.1 EuRoc数据1.2 TUM数据1.3 KITTI数据 2 代码修改2.1 单目2.2 双目2.3 RGB-D 3 运行ROS版ORB-SLAM23.1 单目3.2 双目3.3 RGB-D ORB-SLAM2学习笔记系列&#xff1a; 0 引言 ORB-SLAM2学习笔记1已成功编译安装ROS版本ORB-SLAM2到本地&#xff0c;本…

3、基于Zookeeper实现分布式锁

目录 3.1、Zookeeper安装和相关概念3.1.1 安装启动3.1.2 相关概念3.1.3 Java客户端 3.2 Zookeeper实现分布式锁的思路分析3.3 ZooKeeper分布式锁的基本实现 3.1、Zookeeper安装和相关概念 3.1.1 安装启动 # 解压到/mysoft文件夹下 tar -zxvf zookeeper-3.7.0-bin.tar.gz # 重…

SAP从放弃到入门系列之批次派生-Batch Derivation-Part1

文章目录 一、概述二、系统配置三、主数据3.1 分类主数据3.2 派生规则设置3.2.1发送物料3.2.2 接收物料 四、 测试数据&#xff08;生产订单&#xff09;五、 最后 Batch Derivation翻译成批次派生&#xff08;衍生&#xff09;或批次继承都是问题不大&#xff0c;继承和派生个…

day31贪心算法 用最少数量的箭引爆气球 和无重叠区间

题目描述 题目分析&#xff1a; x轴向上射箭&#xff0c;12一支&#xff0c;重叠的需要一支&#xff0c;3-8一支&#xff0c;7-16一支 返回2&#xff1b; 就是让重叠的气球尽量在一起&#xff0c;局部最优&#xff1b;用一支弓箭&#xff0c;全局最优就是最少弓箭&#xff1b…

ShardingSphere分库分表实战之MySQL主从集群搭建

&#x1f680; ShardingSphere &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&…

uniapp 微信小程序 uni.modal弹框+content内容自定义(内容换行)

效果图&#xff1a; 1、template <view click"showPriceDialog"></view>2、data data() {return {contentText:"",} },3、methods // 价格公示 showPriceDialog: function() {// 吨let minPriceTon 1900let maxPriceTon 3895// 袋let …

23.多项式与非多项式曲线拟合对比(matlab程序)

1.简述 拟合标准&#xff1a; (1)原始数据向量与拟合向量之间的距离最小&#xff0c;该距离的度量一般使用误差平方和表示&#xff0c;即均方误差&#xff1a;R||Q-Y||22 (2)当均方误差最小时&#xff0c;说明构造的拟合向量与原始向量最为接近&#xff0c;这种曲线拟合的方法…

sql中group by 的使用

1、概述 Group By 从字面意义上理解就是根据By指定的规则对数据进行分组&#xff0c;所谓的分组就是将一个数据集划分为若干个小区域&#xff0c;然后针对若干个小区域进行数据处理 2、原始表 3、简单的Group By 示例1 select 类别&#xff0c;数量 as 数量之和 from A gro…

easyui Uncaught TypeError: Cannot read properties of null (reading ‘width‘)

问题描述 在将easyui1.3.6版本替换为1.7版本的时候。只有表头显示出来了&#xff0c;内容并没有显示出来&#xff0c;且报异常。表头也没有按照期望的宽度正常显示。错误提示如下 问题原因及解决办法 该问题出现的原因是因为该表格的表头涉及到跨行跨列问题&#xff0c;原因…

2023年国际高校数学建模竞赛A题:购物网站的数据分析

数据处理&#xff1a; Appendix II的词频统计和数据可视化&#xff1a; 代码: 代码注释完美&#xff0c;可直接运行&#xff0c;简单易用&#xff0c;不懂可以直接问我&#xff0c;在线答疑。 四个题目的代码获取&#xff1a; 可直接运行代码 可直接运行代码 可直接运行…

小程序自定义步骤条实现

效果展示&#xff1a; 支持背景颜色自定义 <view class"hl_steps"><view class"hl_steps_item" wx:for"{{steps}}" wx:key"id"><view class"hl_steps_item_circle_out" style"background-color: {{col…

数据可视化——如何绘制地图

文章目录 前言如何绘制地图添加配置项 根据已有数据绘制地图整体代码展示 前言 前面我们学习了如何利用提供的数据来对数据进行处理&#xff0c;然后以折线图的形式展现出来&#xff0c;那么今天我将为大家分享如何将提数据以地图的形式展现。 如何绘制地图 前面我们绘制折线…

Flutter动画库:animations(路由过渡动画或者页面切换动画)

animations animations 是一个 Flutter 库&#xff0c;它提供了一组用于创建动画效果的工具和组件。这个库的核心重点是路由过渡动画或者页面切换动画 地址 https://pub-web.flutter-io.cn/packages/animations 安装 flutter pub add animations看了下官方文档和官方例子&a…

编程规范—代码风格

先看以下两段代码。 对于计算机来说&#xff0c;这两段代码并没有什么区别&#xff0c;都可以执行&#xff0c;执行结果也一样。但是对于我们人类来说&#xff0c;第二段代码显然看起来更舒适&#xff0c;程序的可读性也更强&#xff0c;跟写作文类似&#xff0c;把所有内容挤在…

RocksDB架构

1、rocksdb是什么? RocksDB中文网 | 一个持久型的key-value存储 rocksdb是一种KV存储引擎&#xff0c;常用于数据库存储数据&#xff0c;无法直接使用&#xff0c;没有提供sql命令&#xff0c;通过调用rocksdb提供的api进行数据库的读写等操作。 rocksdb是以leveldb为基础开…