Spring AOP---面向切面编程由认识到使用

1. AOP

AOP(Aspect-Oriented Programming), 是一种思想, 面向切面编程。

在前文统一异常处理,统一结果返回就是使用了这一思想(都是在集中处理某一类事情, 但又不影响原有代码的正常运行),但他们不是AOP,只是应用了这一思想。

共同点就是:不修改目标方法,而达到了对目标方法功能的增强(就像MybatisPlus对Mybatis) 

AOP是一种思想, 实现它的方式有很多: SpringAOP, AspectJ, CGLIB, ....等

Spring 共有两大核心机制, 一个是 Spring IoC, 一个就是 Spring AOP.

在这句话中,第一个 "Spring" 指的是 Spring Framework,而不是 Spring Boot。

原因是,Spring IoC(控制反转)和 Spring AOP(面向切面编程)是 Spring Framework 的两大核心特性。这些特性是 Spring Framework 的基础,Spring Boot 则是在 Spring Framework 的基础上进行封装和简化配置,使得开发者能更快速地构建应用。

Spring IoC 主要负责对象的管理和依赖注入。

Spring AOP 用于面向切面编程,通过代理机制提供横切关注点(如日志、事务管理等)。

Spring Boot 作为 Spring Framework 的扩展,简化了配置和开发流程,但核心机制(如 IoC 和 AOP)依然是基于 Spring Framework 的。所以当提到 "Spring 的核心机制" 时,通常是指 Spring Framework 中的 IoC 和 AOP。 

关于面向切面编程的讲解会在应用中体现 

2. Spring AOP 入门

2.1 引入 Spring AOP 依赖

Spring AOP 依赖不能在创建项目时引入, 必须手动引入

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

2.2 Spring AOP 简单使用

我们使用 Spring AOP 编写一个程序, 记录接口的执行时长.

@Slf4j
@Aspect
@Component
public class TimeAspect {//公共切点表达式@Pointcut("@annotation(com.cym.spring_aop.aspect.MyAspect)")private void pt(){}@Around("pt()")public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {//记录方法执行前的时间long begin = System.currentTimeMillis();//方法执行Object result = pjp.proceed();//记录方法执行后的时间long end = System.currentTimeMillis();log.info(pjp.getSignature() + ",耗费时间:{}" , end - begin);return result;}
}

如上图所示, 通过 Spring AOP, 我们只需在外部通过 Spring AOP 就可以获取到接口的执行时长. 达到了 "获取执行时长" 这一功能和业务代码的解耦

如果不使用 Spring AOP, 我们就需要在每个接口的起始位置和结束位置获取时间戳, 再计算执行时长, 这样不仅入侵了业务代码, 还需要手动对每个要实现这个功能的接口都编写这些代码(对于懒人是一定不能接受的吧)

3.Spring AOP 详解

3.1 核心概念

Spring AOP 有以下 4 个核心概念:

  1. 切点
  2. 连接点
  3. 通知
  4. 切面

举个例子:
假如你要在所有 Controller 方法执行前打印日志,那么:

切点(Pointcut) = "所有 Controller 里的方法"(切点可以看做是保存了众多连接点的⼀个集合

连接点(JoinPoint)= "Controller 中某个具体的方法"(满⾜切点表达式规则的⽅法

通知(Advice) = "方法执行前,打印日志"(对他说:开工吧)

切面(Aspect) = "拦截所有 Controller 方法,在执行前打印日志"

切⾯(Aspect) = 切点(Pointcut) + 通知(Advice)

 

3.1.1 切点


切点(Pointcut)本质上只是一个筛选规则, 它不会影响代码执行, 也不会真正“拦截”任何东西, 它只是告诉 Spring 要对哪些方法进行拦截, 对哪些方法生效.

3.1.1.1 @Pointcut 定义切点

切点 = @Pointcut 注解(声明切点) + 切点表达式(定义规则)


切点表达式是切点的一部分, 它决定了切点的“筛选规则”.

切点通过切点表达式定义一套规则, 这个规则表名了对哪些方法生效/拦截哪些方法(是一个集合), 描述哪些方法可能成为连接点

可以选择直接在@Around中写表达式:

@Around("execution(* com.cym.spring_aop.controller.*.*(..))")

也可以写在 pt()中:(可以复用,推荐)

    //公共切点表达式@Pointcut("execution(* com.cym.spring_aop.controller.*.*(..))")private void pt(){}@Around("pt()")

 还可以在其他切面中使用(使用时, 需要写出这个切点的全限定名):

com.cym.spring_aop.aspect.TimeAspect.pt()
3.1.1.2 切点表达式


常见的切点表达式有以下两种:

execution: 根据方法的签名来匹配 (如上图所示)
@annotation(......): 根据注解匹配


execution 表达式:

语法:

其中, 访问限定修饰符和异常可以省略.

以execution(* com.cym.spring_aop.controller.*.*(..))为例:

    /*** 1.TestController 下的 public修饰, 返回类型为String ⽅法名为t1, ⽆参⽅法* execution(public String com.example.demo.controller.TestController.t1())* 2.省略访问修饰符* execution(String com.example.demo.controller.TestController.t1())* 3.匹配所有返回类型* execution(* com.example.demo.controller.TestController.t1())* 4.匹配TestController 下的所有⽆参⽅法* execution(* com.example.demo.controller.TestController.*())* 5.匹配TestController 下的所有⽅法* execution(* com.example.demo.controller.TestController.*(..))* 6.匹配controller包下所有的类的所有⽅法* execution(* com.example.demo.controller.*.*(..))* 7.匹配所有包下⾯的TestController* execution(* com..TestController.*(..))* 8.匹配com.example.demo包下, ⼦孙包下的所有类的所有⽅法* execution(* com.example.demo..*(..))*/

@annotation 表达式:


通过 execution 定义切点表达式, Spring AOP 拦截的是符合方法签名规则的方法.

而通过 @annotation 定义切点表达式, Spring AOP 拦截的是标注了特定注解的方法(可以是自定义注解, 也可以是 Spring 提供的注解), 因此更加灵活.

@annotation 中, 需要写出该注解的完全限定名.

1.第一步定义自定义注解

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

@Target(ElementType.METHOD):标识该注解只能作用于方法

@Retention(RetentionPolicy.RUNTIME):表示注解只存活在运行时,在编译好的文件中不存在

2.在需要拦截的方法前加上该注解

3.根据该注解定义切点表达式

    @Pointcut("@annotation(com.cym.spring_aop.aspect.MyAspect)")private void pt(){}@Around("pt()")

3.1.2 连接点

包含在切点表达式中的某个具体的方法, 在程序执行过程中实际被执行的那个方法, 就是一个连接点(即目标方法).

幸运儿,在增强中间执行的方法

  • 切点(Pointcut)是一个筛选规则,用来定义哪些方法(连接点)会被 AOP 代理。

  • 连接点(Join Point)是具体的方法,符合切点规则的方法就是连接点。

3.1.3 通知(Advice)


通知(Advice), 就是 AOP 拦截到目标方法(连接点)后, 具体要做的事/具体要执行的逻辑.

简单来说, 通知就是决定拦截后要做什么事情 .

切点(Pointcut) 只是一个“筛选规则”,它决定哪些方法(连接点)需要被拦截,但它本身不执行任何逻辑.

通知(Advice) 是真正 “干活的人”,它决定拦截后要做什么事情(比如打印日志、权限校验、事务管理等)

3.1.3.1 通知类型

Spring AOP 提供了 5 种常见的通知,不同的通知类型, 执行的时机不同:

• @Around: 环绕通知, 此注解标注的通知⽅法在⽬标⽅法前, 后都被执⾏

• @Before: 前置通知, 此注解标注的通知⽅法在⽬标⽅法前被执⾏

• @After: 后置通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏, ⽆论是否有异常都会执⾏

• @AfterReturning: 返回后通知, 此注解标注的通知⽅法在⽬标⽅法后被执⾏,

有异常不会执⾏

• @AfterThrowing: 异常后通知, 此注解标注的通知⽅法发⽣异常后执⾏

代码演示:

    /*** 前置通知*/@Before("execution(* com.cym.spring_aop.controller.*.*(..))")public void doBefore() {System.out.println("执行doBefore方法");}/*** 后置通知*/@After("execution(* com.cym.spring_aop.controller.*.*(..))")public void doAfter() {System.out.println("执行doAfter方法");}/***抛出异常后通知*/@AfterThrowing("execution(* com.cym.spring_aop.controller.*.*(..))")public void doAfterThrowing() {System.out.println("执行doAfterThrowing方法");}/*** 返回后通知*/@AfterReturning("execution(* com.cym.spring_aop.controller.*.*(..))")public void doAfterReturning() {System.out.println("执行doAfterReturning方法");}/*** 返回后通知*/@Around("execution(* com.cym.spring_aop.controller.*.*(..))")public Object Around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("执行Around方法前..");Object result = pjp.proceed();System.out.println("执行Around方法后..");return result;}

 程序正常运行的情况下, @AfterThrowing 标识的通知方法不会执行, 只有抛出异常时, 该通知方法才会执行.

正常执行:

发现执行顺序:@Around   >  @doBefore  >  @doAferReturning  >  @doAfter  >  @Around

接下来我们添加一个异常 执行一下:

发现执行顺序:@Around   >  @doBefore  >  @doAferThrowing  >  @doAfter 

3.1.4 切面优先级

Spring AOP 允许多个切面作用于同一个目标方法.

当多个切面类, 作用于同一个目标方法(连接点)时, 切面之间是有优先级的:

  • 先执行优先级高的切面中的通知, 后执行优先级低的切面中的通知.

默认的切面优先级是按照名称来排序的:

不加优先级:

加了优先级 

@Order(1)
@Slf4j
@Aspect
@Component
public class Aspect2 {
...
}
@Order(2)
@Slf4j
@Aspect
@Component
public class Aspect3 {
...
}

注意: 

对于 JDK 代理. Spring AOP 只对 public 修饰的方法生效,即切点匹配的目标方法必须是 public, 切面的通知才会生效.
对于 CGLib 代理, Spring AOP 对非 private 非 final 修饰的方法生效,即切点匹配的目标方法不能是 private 或者 final 的.
SpringBoot 默认使用的是 CGLib 代理. 
综上, 如果要对我们项目中的某个方法进行 AOP 拦截通知, 那么这个方法不能是 private 或者 final 修饰的.

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

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

相关文章

专题二十四:虚拟专用网络

一、VPN简介 VPN&#xff08;Virtual Personal Network&#xff09;即虚拟专用网&#xff0c;泛指通过VPN技术在公用网络上构建的虚拟专用网络。VPN用户在此虚拟网络中传输私网流量&#xff0c;在不改变网络现状的情况下实现安全、可靠的连接。其主要功能是在公用网络上建立专…

Milvus(12):分析器

1 分析器概述 在文本处理中&#xff0c;分析器是将原始文本转换为结构化可搜索格式的关键组件。每个分析器通常由两个核心部件组成&#xff1a;标记器和过滤器。它们共同将输入文本转换为标记&#xff0c;完善这些标记&#xff0c;并为高效索引和检索做好准备。 在 Milvus 中&a…

Power Query精通指南1:查询结构设计、数据类型、数据导入与迁移(平面文件、Excel、Web)

文章目录 零、Power Query简介0.1 Power Query 主要功能0.2 Power Query 的优势0.3 Power Query 组件 一、Power Query数据处理基本流程1.1 前期准备1.2 提取1.3 转换1.3.1 Power Query 编辑器界面1.3.2 默认转换1.3.3 自定义转换 1.4 加载1.4.1 自动检测数据类型1.4.2 重命名查…

WebRTC 服务器之Janus概述和环境搭建

1 概述 Janus 是由 Meetecho 开发的通用 WebRTC 服务器&#xff0c;它为构建 WebRTC 应用程序提供了一个模块化框架。服务器目标&#xff1a;Janus WebRTC 网关被设计为轻量级、通用的 WebRTC 服务器&#xff0c;除了实现以下方法外&#xff0c;它本身不提供任何功能&#xff1…

19:常见的Halcon数据格式

遍历文件夹与文件选择 1&#xff09;遍历文件夹&#xff1a; list_files( : : Directory, Options : Files) Directory&#xff1a;目录&#xff08;文件夹路径&#xff09; Options&#xff1a;选项 files 指定搜索的格式为文件 directories 指定搜索的格式为文件夹 re…

QML图像提供器 (Image Provider)

QML 中的图像提供器是一种自定义图像加载机制&#xff0c;允许你从非文件源&#xff08;如数据库、网络或程序生成的内容&#xff09;提供图像数据。 主要类型 QQuickImageProvider - 基础图像提供器 QPixmapImageProvider - 提供 QPixmap 图像 QImageImageProvider - 提供 …

计算机视觉与深度学习 | 双目立体匹配算法理论+Opencv实践+matlab实践

双目立体匹配 一、双目立体匹配算法理论与OpenCV、matlab实践一、双目立体匹配理论二、OpenCV实践三、优化建议四、算法对比与适用场景二、双目立体匹配算法理论及Matlab实践指南一、双目立体匹配理论二、Matlab实践步骤三、算法对比与优化建议四、完整流程示例五、常见问题与解…

AI国学智慧语录视频,条条视频10W+播放量

家人们&#xff01;图书类带货玩法真的非常多&#xff0c;之前也分享过蛮多&#xff0c;例如情感语录、育儿教育、爆款图书金句类、AI历史人物解说类等等。 本期继续来分享一个对于普通人来说&#xff0c;上手相当简单&#xff0c;容易起号&#xff0c;可作为长线深耕的AI带货…

echart图表使用

2、接口编写 该部分代码定义了UserController控制器类&#xff0c;用于处理与用户相关的请求。包含一个用于跳转页面的方法和一个返回用户详细数据&#xff08;以 JSON 格式呈现&#xff09;的接口。前者负责将用户导航至指定页面&#xff0c;后者通过构建ChartVO对象并填充数…

Android短信监控技术实现:合法合规的远程采集方案

一年经验的全栈程序员&#xff0c;目前头发健在&#xff0c;但不知道能撑多久。 该项目已成功部署并稳定运行于企业生产环境&#xff0c;如需个性化定制方案&#xff0c;欢迎联系作者进行深度合作。 文章目录 前言 一、页面设计 1.页面显示 2.代码实现 二、具体代码实现 1.添加…

前端跨域问题怎么在后端解决

目录 简单的解决方法&#xff1a; 添加配置类&#xff1a; 为什么会跨域 1. 什么是源 2. URL结构 3. 同源不同源举&#x1f330; 同源例子 不同源例子 4. 浏览器为什么需要同源策略 5. 常规前端请求跨域 简单的解决方法&#xff1a; 添加配置类&#xff1a; packag…

【中间件】brpc_基础_execution_queue

execution_queue 源码 1 简介 execution_queue.h 是 Apache BRPC 中实现 高性能异步任务执行队列 的核心组件&#xff0c;主要用于在用户态线程&#xff08;bthread&#xff09;中实现任务的 异步提交、有序执行和高效调度。 该模块通过解耦任务提交与执行过程&#xff0c;提…

java学习之数据结构:一、数组

主要是对数组所有的东西进行总结&#xff0c;整理 适合小白~ 目录 1.什么是数组 1.1数组定义 1.2数组创建 1&#xff09;静态创建 2&#xff09;动态创建 1.3数组遍历 1&#xff09;for和while遍历 2&#xff09;foreach遍历 2.数组越界问题及解决 2.1数组越界问题 2…

[Survey]SAM2 for Image and Video Segmentation: A Comprehensive Survey

BaseInfo TitleSAM2 for Image and Video Segmentation: A Comprehensive SurveyAdresshttps://arxiv.org/abs/2503.12781Journal/Time2503Author四川大学&#xff0c;北京大学 1. Introduction 图像分割专注于识别单个图像中的目标、边界或纹理&#xff0c;而视频分割则将这…

用Maven定位和解决依赖冲突

用Maven定位和解决依赖冲突 一、依赖冲突的常见表现二、定位冲突依赖的4种方法2.1 使用Maven命令分析依赖树2.2 使用IDE可视化工具2.3 使用Maven Enforcer插件2.4 运行时分析 三、解决依赖冲突的5种方案3.1 排除特定传递依赖3.2 统一指定版本&#xff08;推荐&#xff09;3.3 使…

穿越数据森林与网络迷宫:树与图上动态规划实战指南

在 C 算法的浩瀚宇宙中&#xff0c;树与图就像是神秘的迷宫和茂密的森林&#xff0c;充满了未知与挑战。而动态规划则是我们探索其中的神奇罗盘&#xff0c;帮助我们找到最优路径。今天&#xff0c;就让我们一起深入这片神秘领域&#xff0c;揭开树与图上动态规划的神秘面纱&am…

UDP / TCP 协议

目录 一、前言&#xff1a; 数据封装与分用&#xff1a; 二、网络协议分层模型&#xff1a; 三、UDP / TCP 协议 UDP 协议&#xff1a; 1、UDP 协议段格式&#xff1a; 2、UDP 的特点&#xff1a; TCP 协议&#xff1a; 1、TCP 协议段格式&#xff1a; 2、TCP 协议的十…

Python 实现的运筹优化系统数学建模详解(动态规划模型)

相关代码链接&#xff1a;https://download.csdn.net/download/heikediguoshinib/90713747?spm1001.2014.3001.5503 一、引言 在计算机科学与数学建模的广阔领域中&#xff0c;算法如同精密的齿轮&#xff0c;推动着问题的解决与系统的运行。当面对复杂的优化问题时&…

langfuse本地安装

目录 安装命令项目准备用openai测试 安装命令 本地&#xff08;docker compose&#xff09;&#xff1a;使用 Docker Compose 在你的机器上于 5 分钟内运行 Langfuse。 # 获取最新的 Langfuse 仓库副本 git clone https://github.com/langfuse/langfuse.git cd langfuse# 运行 …

每天学一个 Linux 命令(35):dos2unix

每天学一个 Linux 命令(35):dos2unix 命令简介 dos2unix 是一个用于将 Windows/DOS 格式的文本文件转换为 Unix/Linux 格式的实用工具。它主要处理行尾符的转换(将 CRLF 转换为 LF),同时也能处理编码问题和字符集转换。这个命令在跨平台文件共享、代码迁移和系统管理场…