【JavaEE】Spring AOP的注解实现

目录

  • 一、AOP 与 Spring AOP
  • 二、Spring AOP简单实现
  • 三、详解Spring AOP
    • 3.1 Spring AOP 核心概念
      • 3.1.1 切点(Pointcut)
      • 3.1.2 连接点(Join Point)
      • 3.1.3 通知(Advice)
      • 3.1.4 切面(Aspect)
    • 3.2 通知类型
    • 3.3 公共切点引用@PointCut
    • 3.4 切点优先级@Order
    • 3.5 切点表达式
      • 3.5.1 execution
      • 3.5.2 @annotation

一、AOP 与 Spring AOP

AOP:Aspect Oriented Programming(⾯向方⾯编程)。是一种对某一类事情集中处理的思想。

Spring AOP:就是对AOP思想的一种实现。

二、Spring AOP简单实现

我们简单实现一个统计每个接口的用时。

引入依赖:

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

写AOP实现:

  • 类使用注解@Aspect修饰
  • 方法参数为ProceedingJoinPoint 类,代表要实现的方法(只能在Around通知下写)
  • 方法使用注解@Around,参数是对应的路径的切点
  • ProceedingJoinPoint 的参数执行proceed方法。
package com.example.library.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect
@Component
@Slf4j
public class TimeAspect {@Around("execution(* com.example.library.controller.*.*(..) )")public Object recordTime(ProceedingJoinPoint  proceedingJoinPoint) throws Throwable {//开始时间long start = System.currentTimeMillis();//执行方法Object result = proceedingJoinPoint.proceed();//结束时间long end = System.currentTimeMillis();log.info("执行时间:"+ (end-start) + "ms");return result;}
}

三、详解Spring AOP

3.1 Spring AOP 核心概念

Spring AOP 核心概念:切点,连接点,通知,切面。

我们以上面的代码来介绍。

3.1.1 切点(Pointcut)

切点:就是告诉程序哪些方法需要使用到接下来的功能。
上面的@Around注解的参数就是切点表达式。

3.1.2 连接点(Join Point)

连接点:满⾜切点表达式规则的⽅法,就是连接点。也就是可以AOP控制的⽅法。

就像上面的代码的连接点就是:com.example.library.controller路径下的所有方法。

切点和连接点的关系:

  • 连接点是满⾜切点表达式的元素。
  • 切点可以看做是保存了众多连接点的⼀个集合。

3.1.3 通知(Advice)

通知:这个Spring AOP方法要实现的功能就是通知。

就像上面的实现一个统计每个接口的用时的需求,就是通知。

3.1.4 切面(Aspect)

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

通过切⾯就能够描述当前AOP程序需要针对于哪些⽅法,在什么时候执⾏什么样的操作。
切⾯既包含了通知逻辑的定义,也包括了连接点的定义。

3.2 通知类型

Spring中AOP的通知类型有以下⼏种:

  • @Around:环绕通知,此注解标注的通知⽅法在⽬标⽅法前,后都被执⾏。
  • @Before:前置通知,此注解标注的通知⽅法在⽬标⽅法前被执⾏。
  • @After:后置通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,⽆论是否有异常都会执⾏。
  • @AfterReturning:返回后通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,有异常不会执⾏。
  • @AfterThrowing:异常后通知,此注解标注的通知⽅法发⽣异常后执⾏。

效果:

package com.example.demoaop;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;@Component
@Slf4j
@Aspect
public class TestAspect {//前置通知@Before("execution(* com.example.demoaop.*.*(..) )")public void testBefore() {log.info("Before 方法执行前");}//后置通知@After("execution(* com.example.demoaop.*.*(..) )")public void testAfter() {log.info("After 方法执行后");}//返回后通知@AfterReturning("execution(* com.example.demoaop.*.*(..) )")public void testAfterReturning() {log.info("AfterReturning 返回后通知");}//抛出异常后通知@AfterThrowing("execution(* com.example.demoaop.*.*(..) )")public void testAfterThrowing() {log.info("AfterThrowing 抛出异常后通知");}//环绕通知@Around("execution(* com.example.demoaop.*.*(..) )")public void testAround(ProceedingJoinPoint pjp) throws Throwable {log.info("Around 方法执行前");Object proceed = pjp.proceed();log.info("Around 方法执行后");}
}

3.3 公共切点引用@PointCut

当我们的切点表达式是一样的时候,像上面我们还是在每一个通知类型的注解中,都使用了相同的表达式。
我们就可以使用方法注解@PointCut将切点表达式提取出来,然后后面使用只需要写方法名即可。
在其他切点类中也可以调用,需要将@PointCut注解所在类的路径写出来。

package com.example.demoaop;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component
@Slf4j
@Aspect
public class TestAspect {@Pointcut("execution(* com.example.demoaop.*.*(..) )")public void pc(){}//前置通知@Before("pc()")public void testBefore() {log.info("TestAspect Before 方法执行前");}//后置通知@After("pc()")public void testAfter() {log.info("TestAspect After 方法执行后");}
}
package com.example.demoaop;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Component
@Slf4j
@Aspect
public class TestAspect2 {//前置通知@Before("com.example.demoaop.TestAspect.pc()")public void testBefore() {log.info("TestAspect2 Before 方法执行前");}//后置通知@After("pc()")public void testAfter() {log.info("TestAspect2 After 方法执行后");}}

结果:
可以看见生效了,而且在其他切点类中只有加上了路径的才生效了。

3.4 切点优先级@Order

我们定义3个一样的切点类,看他们的输出顺序:

存在多个切⾯类时,默认按照切⾯类的类名字⺟排序:

  • @Before 通知:字⺟排名靠前的先执⾏
  • @After 通知:字⺟排名靠前的后执⾏
    但这种⽅式不⽅便管理,我们的类名更多还是具备⼀定含义的。
    Spring 给我们提供了⼀个新的注解,来控制这些切⾯通知的执⾏顺序:@Order

我们将切点类的优先级换一下:

@Component
@Slf4j
@Aspect
@Order(1)
public class TestAspect3 {//前置通知@Before("com.example.demoaop.TestAspect.pc()")public void testBefore() {log.info("TestAspect3 Before 方法执行前");}//后置通知@After("com.example.demoaop.TestAspect.pc()")public void testAfter() {log.info("TestAspect3 After 方法执行后");}}
@Component
@Slf4j
@Aspect
@Order(2)
public class TestAspect2 {//前置通知@Before("com.example.demoaop.TestAspect.pc()")public void testBefore() {log.info("TestAspect2 Before 方法执行前");}//后置通知@After("com.example.demoaop.TestAspect.pc()")public void testAfter() {log.info("TestAspect2 After 方法执行后");}}
@Component
@Slf4j
@Aspect
@Order(3)
public class TestAspect {@Pointcut("execution(* com.example.demoaop.*.*(..) )")public void pc(){}//前置通知@Before("pc()")public void testBefore() {log.info("TestAspect Before 方法执行前");}//后置通知@After("pc()")public void testAfter() {log.info("TestAspect After 方法执行后");}
}

执行结果:

规律:
@Order 注解标识的切⾯类,执⾏顺序如下:

  • @Before 通知:数字越⼩先执⾏
  • @After 通知:数字越⼤先执⾏

像下图的表示,箭头代表执行过程:

3.5 切点表达式

切点表达式用来描述切点,常有以下两种类型的切点表达式:execution 和 @annotation

3.5.1 execution

语法:

execution(<访问修饰限定符> <返回类型> <包名.类名.方法名(方法参数)> <异常>)

含义:

  • 访问修饰限定符:表示切点对应的方法的访问修饰限定符
  • 返回类型:表示切点对应的方法的返回类型
  • 包名.类名.方法名(方法参数):表示切点对应的方法的路径及参数
  • 异常:表示切点对应的方法抛出的异常
  • 访问修饰限定符 和 异常 可以省略

切点表达式⽀持通配符表达:

  1. :* 匹配任意字符,只匹配⼀个元素(返回类型,包,类名,⽅法或者⽅法参数)
    1.1. 包名使⽤ * 表⽰任意包(⼀层包使⽤⼀个 * )
    1.2. 类名使⽤ * 表⽰任意类
    1.3. 返回值使⽤ * 表⽰任意返回值类型
    1.4. ⽅法名使⽤ * 表⽰任意⽅法
    1.5. 参数使⽤ * 表⽰⼀个任意类型的参数
  2. : 两个点 . . 匹配多个连续的任意符号,可以通配任意层级的包,或任意类型,任意个数的参数
    2.1. 使⽤ . . 配置包名,标识此包以及此包下的所有⼦包
    2.2. 可以使⽤ . . 配置参数,任意个任意类型的参数

例子:

  • TestController 下的 public修饰,返回类型为String ⽅法名为t1的⽆参⽅法
execution(public String com.example.demo.TestController.t1())
  • 匹配 TestController 下的所有⽆参⽅法
execution(* com.example.demo.TestController.*())
  • 匹配controller包下所有的类的所有⽅法
execution(* com.example.demo.controller.*.*(..))

3.5.2 @annotation

当我们要落实到不同类下个几个方法,用上面的execution就有点捉襟见肘。
我们就可以使用⾃定义注解的⽅式以及另⼀种切点表达式 @annotation 来描述这⼀类的切点。

自定义注解:

  • 在自定义类的时候选择annotation:
  • 然后就跟我们前面使用的注解一样包含,生命周期@Retention,作用范围@Target,交给Spring管理。
@Component
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}

定义切面类:

  • 使用@Aspect注解修饰,
  • 交给Spring管理
  • 在通知类型的注解中使用:@annotation(自定义注解路径) 作为参数。
@Slf4j
@Component
@Aspect
public class MyAspectDemo {@Around("@annotation( com.example.demoaop.MyAspect)")public void around(ProceedingJoinPoint pjp) throws Throwable {log.info("annotation 运行前");pjp.proceed();log.info("annotation 运行后");}
}

通过上面的方法,使用了自定义注解修饰的方法,就可以添加切面类的通知。

@RequestMapping("/test")
@RestController
@Slf4j
public class Test {@RequestMapping("/f1")public String f1() {log.info("f1");return "s1";}@MyAspect@RequestMapping("/f2")public Integer f2() {log.info("f2");return 1;}@RequestMapping("/f3")public Boolean f3() {log.info("f3");return false;}
}

访问f2 f1 f3 的结果:

除了上面讲的基于注解的方式实现Spring AOP 还有远古的通过xml和代理的方式实现。参考Spring AOP其它实现方式

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

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

相关文章

协作开发攻略:Git全面使用指南 — 结语

协作开发攻略&#xff1a;Git全面使用指南 — 结语 Git 是一种分布式版本控制系统&#xff0c;用于跟踪文件和目录的变更。它能帮助开发者有效管理代码版本&#xff0c;支持多人协作开发&#xff0c;方便代码合并与冲突解决&#xff0c;广泛应用于软件开发领域。 文中内容仅限技…

如何用AI主动突出画面主体!涂鸦新方案助剪辑、工业巡检、医疗影像等领域,实现自动追踪+智能放大

随着智能 IPC 设备&#xff08;如安防摄像头、宠物陪伴机器人、婴儿监视器等&#xff09;日益普及&#xff0c;越来越多的生活场景被实时记录。然而在实际使用中&#xff0c;由于设备安装位置不当、广角镜头视野过大等原因&#xff0c;经常会出现拍摄主体占比过小的问题&#x…

数据湖DataLake和传统数据仓库Datawarehouse的主要区别是什么?优缺点是什么?

数据湖和传统数据仓库的主要区别 以下是数据湖和传统数据仓库的主要区别&#xff0c;以表格形式展示&#xff1a; 特性数据湖传统数据仓库数据类型支持结构化、半结构化及非结构化数据主要处理结构化数据架构设计扁平化架构&#xff0c;所有数据存储在一个大的“池”中多层架…

当智驾成标配,车企暗战升级|2025上海车展

文&#xff5c;刘俊宏 编&#xff5c;王一粟 智能化无处不在的2025年上海车展&#xff0c;回归了卖车的初衷。 光锥智能在展会暴走两天&#xff0c;最大的感触是今年的车展少了争奇斗艳&#xff0c;多了些许务实。 回顾智能汽车时代的三场重要车展。2023年的上海车展充满了…

如何在Spring Boot中禁用Actuator端点安全性

在 Spring Boot 应用中&#xff0c;Spring Boot Actuator 提供了一系列用于监控和管理应用的端点&#xff08;如 /actuator/health、/actuator/metrics&#xff09;&#xff0c;这些端点默认可能受到 Spring Security 的保护&#xff0c;要求身份验证或授权。然而&#xff0c;在…

【mongodb】系统保留的数据库名

目录 1. admin2. config3. local4. test&#xff08;非严格保留&#xff0c;但常作为默认测试数据库&#xff09;5. 注意事项6. 其他相关说明 1. admin 1.用途&#xff1a;用于存储数据库的权限和用户管理相关数据。2.特点&#xff1a;该数据库是 MongoDB 的超级用户数据库&am…

Redis是单线程的,如何提高多核CPU的利用率?

一句话回答&#xff1a; Redis 是单线程处理客户端命令&#xff0c;但可以通过 多实例部署、I/O 多路复用、后台线程 Redis 6 的 I/O Thread 支持&#xff0c;来充分利用多核 CPU。 一、Redis 单线程 ≠ 整个 Redis 都是单线程&#xff01; Redis 主要的 网络事件 命令执行 …

关于mysql的事务和索引

1. 事务四大特性&#xff08;ACID&#xff09; 原子性&#xff1a;事务的操作要么全部成功&#xff0c;要么全部失败回滚&#xff0c;不可分割。 一致性&#xff1a;事务执行前后&#xff0c;数据必须满足业务规则&#xff08;如账户总额不变&#xff09;。 隔离性&#xff1…

【Python】保持Selenium稳定爬取的方法(防检测策略)

selenium 防检测策略的方法汇总&#xff1a; 合理设置延迟&#xff1a;请求间添加随机延迟 (2-10秒) 限制爬取频率&#xff1a;控制每小时/每天的请求量 轮换用户代理&#xff1a;准备至少10个不同的User-Agent 使用住宅代理&#xff1a;优先选择高质量的住宅代理IP 处理验…

SpringSecurity源码解读AbstractAuthenticationProcessingFilter

一、介绍 AbstractAuthenticationProcessingFilter 是 Spring Security 框架里的一个抽象过滤器,它在处理基于表单的认证等认证流程时起着关键作用。它继承自 GenericFilterBean,并实现了 javax.servlet.Filter 接口。此过滤器的主要功能是拦截客户端发送的认证请求,对请求…

什么是DDD?为什么它正在取代传统架构?

什么是DDD&#xff1f;为什么它正在取代传统架构&#xff1f; 1. 传统开发模式的痛点 在经典的MVC架构中&#xff0c;开发流程往往从数据库表结构设计开始&#xff0c;业务逻辑散落在Service层&#xff0c;随着需求迭代容易形成「大泥球」代码&#xff1a; 实体类变成纯粹的…

基于外部中中断机制,实现以下功能: 1.按键1,按下和释放后,点亮LED 2.按键2,按下和释放后,熄灭LED 3.按键3,按下和释放后,使得LED闪烁

题目&#xff1a; 参照外部中断的原理和代码示例,再结合之前已经实现的按键切换LED状态的实验&#xff0c;用外部中断改进其实现。 请自行参考文档《中断》当中&#xff0c;有关按键切换LED状态的内容, 自行连接电路图&#xff0c;基于外部中断机制&#xff0c;实现以下功能&am…

在SQL中,FROM子句中的子查询必须指定别名,即使后续未引用该别名

FROM子句中的子查询必须指定别名 示例错误示例及原因&#xff1a;总结&#xff1a; 在SQL中&#xff0c; FROM子句中的子查询必须指定别名&#xff0c; 即使后续未引用该别名 示例 查询馆藏图书最多的作者姓名及馆藏数量 SELECT 作者, COUNT(图书编号) AS 馆藏数量 FROM 图…

问道数码兽 怀旧剧情回合手游源码搭建教程(反查重优化版)

本文将对"问道数码兽"这一经典卡通风格回合制手游的服务端部署与客户端调整流程进行详细拆解&#xff0c;适用于具备基础 Windows 运维和手游源码调试经验的开发者参考使用。教程以实战为导向&#xff0c;基于原始说明内容重构优化&#xff0c;具备较高的内容查重避重…

Shell脚本-for循环应用案例

在Shell脚本编程中&#xff0c;for循环是一种强大的工具&#xff0c;用于处理重复性任务。无论是批量处理文件、遍历目录内容还是简单的计数任务&#xff0c;for循环都能提供简洁而有效的解决方案。本文将通过几个实际的应用案例来展示如何使用for循环解决具体的编程问题。 案…

Chrmo手动同步数据

地址栏输入 chrome://sync-internals分别点击这2个按钮即可触发手动同步

为什么圆形在GeoJSON中被表示为多边形(Polygon)而不是圆形类型

GeoJSON规范中没有"圆形"类型 GeoJSON是一种用于表示地理空间数据的标准格式,它的规范中只定义了以下几种基本几何类型: Point (点) LineString (线) Polygon (多边形) MultiPoint (多点) MultiLineString (多线) MultiPolygon (多多边形) GeometryCollection (几…

大数据组件学习之--Kafka 安装搭建

一、前置环境 在搭建kafka之前&#xff0c;请确认自己的hadoop、zookeeper是否搭建完成且可正常运行 二、下载并上传安装包&#xff08;链接为百度网盘&#xff09; kafka安装包 tar -zxvf /opt/software/kafka_2.12-2.4.1.tgz -C /opt/module/ 进入解压后的目录更改文件名…

PyQt6基础_pyqtgraph_折线图with缩放调节

目录 字符型横坐标代码 折线图代码 运行 创建新类&#xff0c;继承pg.PlotWidget&#xff0c;在新类中实现业务内容&#xff0c;重写pg.PlotWidget中的wheelEvent方法并使用业务数据实现比较理想的缩放状态。 字符型横坐标代码 class StrAxisItem(pg.AxisItem):def __init…

联邦元学习实现个性化物联网的框架

随着数据安全和隐私保护相关法律法规的出台&#xff0c;需要直接在中央服务器上收集和处理数据的集中式解决方案&#xff0c;对于个性化物联网而言&#xff0c;训练各种特定领域场景的人工智能模型已变得不切实际。基于此&#xff0c;中山大学&#xff0c;南洋理工大学&#xf…