Spring >> Spring AOP

AOP 与 Spring AOP

AOP:面向切面编程。是一种思想,对某一类事情的集中处理。

例如现在大多数平台的用户登录都是有权限效验的,而对于平台页面的操作,除过登录,注册或者一些简单的,大多都是需要验证用户有没有登录,没有登录的话就没有权限去做一些操作。在这个效验的过程中,没有使用 AOP 之前是需要对每个方法都实现效应操作的,这样以来就会增加代码的重复度,效率减少,而实现 AOP 之后,只需要在某一个类中单独配置切面,这样一来就不用在每个方法中实现效验代码了。

Spring AOP:是对该思想的具体实现

也就是说当如上述那样功能统一,且使用的地方较多的时候,就可以考虑使用 AOP 进行统一处理了。
除了上述统一用户登录效验以外,AOP 还可以实现

  • 统一日志记录:利用 AOP 实现日志记录。通过 AOP 当一个方法执行的时候会自动打印出自定日志;
  • 事务管理:通过 @Transaction 注解实现对异常事务进行回滚操作,免去了重复的事务管理逻辑。@Transaction 注解就是基于 AOP 实现;
  • 性能统计:利用 AOP 在目标方法执行前后统计方法执行的时间;
  • 权限控制:使用 AOP 在执行目标方法前判断用户是否具备所需要的权限,具备就执行目标方法,不具备就不执行(也就是上述举例);
  • 缓存管理:使用 AOP 在目标方法执行前进行缓存读取和更新;等等、等等。

AOP 组成:

  • 切面:定义事件(即当前 AOP 是干啥的),例如当前 AOP 是 用户登录效验 事件,对用户登录效验进行集中处理;
  • 切点:定义具体规则。例如定义用户登录拦截规则,哪些接口/方法需要判断用户权限,哪些不需要;
  • 通知:AOP 执行的具体方法;
  • 连接点:有可能触发切点的所有点;例如所有的接口或者方法都有可能触发。

AOP 的实现原理:

  • 常见的实现方式是通过动态代理、字节码操作进行实现
  • Spring AOP 基于动态代理实现

Spring AOP 的实现步骤:

  1. 添加 Spring AOP 依赖;
  2. 定义切面;
  3. 定义切点;
  4. 实现通知。

通知的类型:
通知定义的是被拦截的⽅法具体要执⾏的业务(即当一个方法被拦截了,会在拦截时执行一些业务方法,而通知就是定义在这些业务方法上的),⽐如⽤户登录权限验证⽅法就是具体要执⾏的业务,此时通知就会定义到该方法上,当需要验证登录权限时,⽤户登录权限验证⽅法就会自动调用。Spring AOP 中,可以在⽅法上使⽤以下注解,设置⽅法为通知⽅法,在满⾜条件后会通知本⽅法进⾏调⽤:

  • 前置通知:使⽤ @Before 注解,通知⽅法会在⽬标⽅法调⽤之前执⾏。
  • 后置通知:使⽤ @After 注解,通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤。
  • 返回之后通知:使⽤ @AfterReturning 注解,通知⽅法会在⽬标⽅法返回后调⽤。
  • 抛异常后通知:使⽤ @AfterThrowing 注解,通知⽅法会在⽬标⽅法抛出异常后调⽤。
  • 环绕通知:使⽤ @Around 注解,通知方法包裹了被通知的⽅法,在被通知的⽅法通知之前和调⽤之后执⾏⾃定义的⾏为。

Spring AOP 的使用

1. 对通知的几种类型进行应用

案例:使用 Spring AOP 实现对 UserController 类中的方法进行拦截处理
1. 添加依赖:

创建 Spring Boot 项目,添加 AOP 依赖:

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

2. 定义切面:

在类上面添加 @Aspect 注解
在这里插入图片描述
3. 定义切点
先实现一个 UserController 类,用于后续拦截进行测试。若被拦截了就不会执行该类里面的方法

@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/setUser")public String setUser(){System.out.println("[UserController] do setUser");return "[UserController] setUser";}@RequestMapping("/getUser")public String getUser(){System.out.println("[UserController] do getUser");return "[UserController] getUser";}
}

定义切点:

@Aspect  // 定义切面,表示此类为一个切面
@Component
public class UserAspect {// 定义切点,使⽤ AspectJ 切点表达式@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")public void pointCut(){}   // 切点是没有具体实现的
}

在这里插入图片描述

切点表达式:
切点表达式由切点函数组成,其中 execution() 是最常⽤的切点函数,⽤来匹配⽅法,语法为:

execution(<修饰符><返回类型><..⽅法(参数)><异常>)
其中修饰符和异常一般都会省略
三种通配符:
1.  * :匹配任意字符,只匹配⼀个元素(包,类,或⽅法,⽅法参数)
2.  .. : 匹配任意字符,可以匹配多个元素  ,在表示类时,必须和  * 联合使⽤
3.  + : 表示按照类型匹配指定类的所有类,必须跟在类名后⾯,如  com.demo.Animal+ ,表示继承该类的所有⼦类包括本身

4. 定义通知
上面也提到了,通知有五中,而前4种(前置通知、后置通知、返回通知、异常通知)实现方法都是类似的,只需要在方法前面加各自的注解即可。而环绕通知会单独实现。
下面就先针对 UserController 这个类,实现前置通知和后置通知,返回通知和异常通知与前置、后置通知是类似的,只需要改变注解即可,在此就不另外实现。
定义前置通知:

@Aspect  // 定义切面,表示此类为一个切面
@Component
public class UserAspect {// 定义切点@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")public void pointCut(){}   // 切点是没有具体实现的// 通知,前置通知@Before("pointCut()") // 双引号里面写的是 该通知针对哪个切点public void doBefore(){System.out.println("[UserAspect] 执行了前置通知");}
}

启动项目,通过 URL 运行结果如下:

在这里插入图片描述

在这里插入图片描述

就会发现拦截器已经拦截成功了。当执行 UserController 里面的方法时,会先执行通知方法。

定义后置通知

@Aspect  // 定义切面,表示此类为一个切面
@Component
public class UserAspect {// 定义切点@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")public void pointCut(){}   // 切点是没有具体实现的// 通知,前置通知@Before("pointCut()") // 双引号里面写的是 该通知针对哪个切点public void doBefore(){System.out.println("[UserAspect] 执行了前置通知");}// 通知,后置通知@After("pointCut()")public void doAfter(){System.out.println("[UserAspect] ---执行后置通知---");}
}

启动项目,查看输出结果:

在这里插入图片描述
顾名思义,当使用后置通知的时候,通知是在 UserController 中的方法执行之后才进行通知的。

定义环绕通知:

@Aspect  // 定义切面,表示此类为一个切面
@Component
public class UserAspect {// 定义切点@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")public void pointCut(){}   // 切点是没有具体实现的// 通知,前置通知@Before("pointCut()") // 双引号里面写的是 该通知针对哪个切点public void doBefore(){System.out.println("[UserAspect] 执行了前置通知");}// 通知,后置通知@After("pointCut()")public void doAfter(){System.out.println("[UserAspect] ---执行后置通知---");}// 环绕通知@Around("pointCut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {   // ProceedingJoinPoint:获取连接点这个对象,然后通过连接点的对象中的 proceed 方法来对目标方法进行执行System.out.println("环绕通知执行前");Object result = joinPoint.proceed(); // joinPoint.proceed(): 执行目标方法System.out.println("环绕通知执行后");return result;  // 将对象返回给框架}
}

启动项目,输出结果如下:

在这里插入图片描述
在这里插入图片描述

可以发现,

  • 环绕通知在执行前后都可以进行通知
  • 环绕通知会在前置通知前、后置通知后进行通知

2. 对方法的性能进行检测

案例:使用 Spring AOP 实现对 UserController 类中的方法性能进行统计
还是按照上述先定义切面,定义切点、定义通知,实现代码如下:

@Slf4j  // 此处使用 lombok @Slf4j 注解自定义打印日志
@Aspect  // 定义切面,表示此类为一个切面
@Component
public class UserAspect {// 定义切点,使⽤ AspectJ 切点表达式@Pointcut("execution(* com.example.demo.controller.UserController.* (..))")public void pointCut(){}   // 切点是没有具体实现的// 环绕通知实现方法性能统计@Around("pointCut()")public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {/** 1. 记录开始时间* 2. 执行目标方法* 3. 记录结束时间* 4. 计算消耗时间* */long start = System.currentTimeMillis();Object result = joinPoint.proceed();log.info(joinPoint.getSignature()+" : " + "[UserAspect] cost time: " +(System.currentTimeMillis() - start)+ "ms");  // 使用 Slf4j 日志输出  joinPoint.getSignature() 是用于获取哪个方法的执行的return result;}
}

启动项目,输入 URL ,输出结果如下:

在这里插入图片描述
这样以来就会将方法执行的时间统计出来,方便对代码的性能进行可视化。

Spring AOP 实现原理(基于动态代理)

Spring AOP 是通过动态代理的⽅式,在运⾏期将 AOP 代码织⼊到程序中的,它的实现⽅式有两种: JDK Proxy 和 CGLIB。
Spring AOP ⽀持 JDK Proxy 和 CGLIB ⽅式实现动态代理。默认情况下,实现了接口的类,使用 AOP 会基于 JDK Proxy ⽣成代理类,没有实现接⼝的类,会基于 CGLIB ⽣成代理类。

补充:
织入:织入是将切面和目标对象连接起来的过程,也就是将通知应用到切点匹配的连接点上。常见的织入时间有两种,分别是编译期织入和运行期织入。

JDK Proxy 和 CGLIB 的区别

  • JDK Proxy 动态代理基于接口,要求目标对象实现接口;CGLIB 动态代理基于类,可以代理没有实现接口的目标对象;
  • CGLIB 动态代理无法代理 final 类和 final 方法;JDK Proxy 动态代理可以代理任意类;
  • JDK Proxy 动态代理性能相对较高,生成代理对象速度较快;CGLIB 动态代理性能相对较低,生成代理对象速度较慢。

Spring AOP 解决了什么问题(作用)

AOP 可以将横切关注点(如日志记录、事务管理、权限控制等)从核心业务逻辑中分离出来,实现关注点的分离。

  • 横切关注点:多个类或对象中的公共行为(如日志记录、事务管理、权限控制、接口限流、接口幂等等)

这样以来就避免在每个类或对象中对一些功能相同的代码进行重复实现,降低代码冗余,实现代码复用和解耦,提高代码的可维护性和可扩展性。

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

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

相关文章

【linux软件基础知识】如何使用 run_list 字段将任务放入就绪队列中

在给定的代码片段中,struct task_struct 表示内核中任务或进程的进程控制块 (PCB)。 run_list 字段的类型为 struct list_head,这表明它是链表实现的一部分。 run_list字段在Linux内核中常用来表示任务在调度队列中的位置,例如就绪队列或各种优先级队列。 init_task是一个…

查询新加 字段不返回数据要看 有没有 AllInfoResultMap 有要再里面加字段

查询新加 字段不返回数据要看 有没有 AllInfoResultMap 有要再里面加字段

Linux安装配置CGAL,OpenCV和Gurobi记录

安装Qt&#xff0c;查看当前的Qt版本&#xff0c;需要至少满足v5.12 qmake -v安装CGAL&#xff0c;The Computational Geometry Algorithms Library (cgal.org) CGAL v5.6.1&#xff1a;https://github.com/CGAL/cgal/releases/download/v5.6.1/CGAL-5.6.1.tar.xz 确保C编译…

探索互联网医院系统源码:开发在线药房小程序实战教学

今天&#xff0c;笔者将与大家一同深入探讨互联网医院系统的源码结构&#xff0c;并通过开发在线药房小程序的实战教学&#xff0c;为读者提供一种学习和理解这一领域的途径。 一、互联网医院系统源码解析 1.技术选型 互联网医院系统的开发离不开合适的技术选型&#xff0c;…

AI + Web3 如何打造全新创作者经济模型?

可编程 IP 的兴起&#xff0c;借助人工智能极大提高创作效率和效能&#xff0c;让 Web3 用户体会到了自主创作和产品制作的乐趣。然而&#xff0c;你知道 AI 时代来临的背景下&#xff0c;创作者经济模型又该如何在 Web3 技术的加持下走向更成熟的运作轨道吗&#xff1f;第 43 …

【保姆级教程】VMware Workstation Pro的虚拟机导入vritualbox详细教程

解决方案 1、OVF格式2、VMX格式 1、OVF格式 选定需要导出的虚拟机&#xff08;关闭或者挂起状态下&#xff09;依次选择文件-导出为ovf 在Vritualbox导入刚刚导出的.ovf文件 更改路径&#xff0c;按实际需要修改 成功导入 2、VMX格式 如果在VMware Workstation Pro导出的…

【博士生必看】论文润色大揭秘!

&#x1f4dd; 投稿拒稿&#xff1f;语言不过关&#xff1f;别怕&#xff0c;我来支招&#xff01;&#x1f469;‍&#x1f393; &#x1f31f; 我的论文润色经历&#xff0c;从拒稿到接收的逆袭之路&#xff01;✨ &#x1f449; 【论文润色&#xff0c;我选了它】 我选择了…

基于SSM+Vue的物流管理系统

运行截图 获取方式 Gitee仓库

【第19章】spring-mvc之全局异常处理

文章目录 前言一、全局异常处理1. 前端2. 后端 二、常见错误页1.增加界面2.web.xml3.异常处理4.效果 总结 前言 例如&#xff1a;随着人工智能的不断发展&#xff0c;机器学习这门技术也越来越重要&#xff0c;很多人都开启了学习机器学习&#xff0c;本文就介绍了机器学习的基…

从零开始的软件测试学习之旅(七)接口测试流程及原则案例

接口测试三要素及案例 接口测试介绍接口预定义接口测试的主要作用测试接口流程如下接口测试三要素接口测试分类RESTful架构风格RESTful架构三要素要素一要素二要素三 RESTful架构风格实现restful架构案例接口测试流程接口测试原则功能测试自动化测性能测试 复习复盘 接口测试介…

Qt---事件

一、Qt中的事件 鼠标事件 鼠标进入事件enterEvent 鼠标离开事件leaveEvent 鼠标按下mousePressEvent ( QMouseEvent ev) 鼠标释放mouseReleaseEvent 鼠标移动mouseMoveEvent ev->x()&#xff1a;坐标 ev->y()&#xff1a;y坐标 ev->bu…

【计算机毕业设计】基于微信小程序高校寻物平台

随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于 微信小程序的高校寻物平台的开发全过程。通过分析基于微信小程序的高校寻物平台管理的不足&#xff0c;创建了一个计算机管理基于微信小程序的高校寻物平台的方案…