Spring AOP(面向切面编程)的详细讲解

1.什么是 AOP?

AOP(Aspect Oriented Programming):⾯向切⾯编程,它是⼀种思想,它是对某⼀类事情的集中处理
AOP是一种思想,而Spring AOP是一个实现了AOP的思想框架,他们的关系和IOC与DI类似

2.为什要⽤ AOP?

想象⼀个场景,我们在做后台系统时,除了登录和注册等⼏个功能不需要做⽤户登录验证之外,其他⼏乎所有⻚⾯调⽤的前端控制器(Controller)都需要先验证⽤户登录的状态,那这个时候我们要怎么处理呢?

我们之前的处理⽅式是每个 Controller都要写⼀遍⽤户登录验证,然⽽当你的功能越来越多,那么你要写的登录验证也越来越多,⽽这些⽅法⼜是相同的,这么多的⽅法就会代码修改和维护的成本。那有没有简单的处理⽅案呢?

有人说可以抽取一个公共方法出来,每次需要验证的时候去调用这个方法就好了
这样做是可以的,但是还存在一个问题,如果登陆方法的参数发生改变,那么每个调用者都需要跟着修改,这样就会变得复杂,并且代码之间耦合严重,我们要尽量避免这种情况

开发的三个阶段:

  1. 初级阶段:每个方法都实现
  2. 中级阶段:抽取公共方法
  3. 高级阶段:采用AOP的方式

所以,对于这种功能统⼀,且使⽤的地⽅较多的功能,就可以考虑 AOP来统⼀处理了
除了统⼀的⽤户登录判断之外,AOP 还可以实现:

  • 统⼀⽇志记录
  • 统⼀⽅法执⾏时间统计(在性能优化阶段,监控流量,接口的响应时间等甚至每个方法的响应时间,为整个项目的性能进行优化)
  • 统⼀的返回格式设置 (对于接口的返回格式,基本上都是code,message,data)
  • 统⼀的异常处理
  • 事务的开启和提交等

也就是说使⽤AOP 可以扩充多个对象的某个能⼒,所以 AOP 可以说是 OOP(Object OrientedProgramming,⾯向对象编程)的补充和完善

3.Spring AOP 应该怎么学习呢?

Spring AOP 学习主要分为以下 3 个部分:

  1. 学习 AOP 是如何组成的?也就是学习 AOP 组成的相关概念。
  2. 学习 Spring AOP 使⽤。
  3. 学习 Spring AOP 实现原理。

3.1 AOP 组成

1 切⾯(Aspect)

切⾯(Aspect)由切点(Pointcut)和通知(Advice)组成,它既包含了横切逻辑的定义,也包括了连接点的定义。

切⾯是包含了:通知、切点和切⾯的类,相当于 AOP 实现的某个功能的集合

2 连接点(Join Point)

应⽤执⾏过程中能够插⼊切⾯的⼀个点,这个点可以是⽅法调⽤时,抛出异常时,甚⾄修改字段时。切⾯代码可以利⽤这些点插⼊到应⽤的正常流程之中,并添加新的⾏为

连接点相当于需要被增强的某个 AOP 功能的所有⽅法

3 切点(Pointcut)

Pointcut 的作⽤就是提供⼀组规则(使⽤ AspectJ pointcut expression language 来描述)来匹配 Join Point,给满⾜规则的 Join Point 添加 Advice

切点相当于保存了众多连接点的⼀个集合(如果把切点看成⼀个表,⽽连接点就是表中⼀条⼀条 的数据)

4 通知(Advice)

通知:定义了切⾯是什么,何时使⽤,其描述了切⾯要完成的⼯作,还解决何时执⾏这个⼯作的问题

AOP是对于同一类事情(范围)集中处理(处理的内容是什么)
对于AOP而言,处理的内容就是通知,切⾯的⼯作被称之为通知

Spring 切⾯类中,可以在⽅法上使⽤以下注解,会设置⽅法为通知⽅法,在满⾜条件后会通知本⽅法进⾏调⽤:

  • 前置通知使⽤ @Before:通知⽅法会在⽬标⽅法调⽤之前执⾏。
  • 后置通知使⽤ @After:通知⽅法会在⽬标⽅法返回或者抛出异常后调⽤
  • 返回之后通知使⽤ @AfterReturning:通知⽅法会在⽬标⽅法返回后调⽤。
  • 抛异常后通知使⽤ @AfterThrowing:通知⽅法会在⽬标⽅法抛出异常后调⽤。
  • 环绕通知使⽤ @Around:通知包裹了被通知的⽅法,在被通知的⽅法通知之前和调⽤之后执 ⾏⾃定义的⾏为。
    在jointPoint.proceed()前后都可以通知
    注意:返回结果需要自己定义

4.我们就以用户登陆验证来举例:

1.首先我们需要创建一个springMVC的项目

参考博客:
spring项目的创建

2.在pop.xml中添加以下配置

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-bo
ot-starter-aop -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3.创建一个UserController类,模拟用户需要执行的一些方法

/*** 用户需要调用的一些方法*/
@Slf4j // 用来打印日志的
@RestController
@RequestMapping("/user")
public class UserController {// 获取用户信息@RequestMapping("/get")public String getInfo() {return "获取信息";}// 注册@RequestMapping("/reg")public String reg() {return "注册";}// 登录@RequestMapping("/log")public String log() {return "登录";}}

4.定义切面和切点

切面就是具体要处理的某一类问题,(比如用户登录权限验证就是一个具体的问题)切点的作用就是制定一组规则,(比如在这里我们就要制定哪些方法可以走到切面的类里面来,就是拦截那些需要验证用户身份的操作)

@pointcut()注解内的切点表达式说明

1.AspectJ ⽀持三种通配符

在这里插入图片描述
切点表达式由切点函数组成,其中 execution() 是最常⽤的切点函数,⽤来匹配⽅法,语法为:
execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)
在这里插入图片描述

2.表达式示例

在这里插入图片描述

@Slf4j // 用来打印日志的
@Component // 将此类交给spring容器来管理
@Aspect // 表示此类为一个切面
public class LoginAspect {// 定义一个切点@Pointcut("execution(* com.example.springaop.controller.UserController.* (..))")public void pointcut() {//其中 pointcut ⽅法为空⽅法,它不需要有⽅法体,此⽅法名就是起到⼀个“标识”的作⽤,标识下⾯的通知⽅法具体指的是哪个切点(因为切点可能有很多个)}
}

其中 pointcut ⽅法为空⽅法,它不需要有⽅法体,此⽅法名就是起到⼀个“标识”的作⽤,标识下⾯的通知⽅法具体指的是哪个切点(因为切点可能有很多个)

5.此时我们来测试一下通知方法注解

通知里就是要定义被切点拦截过来的方法具体要执行的业务,比如用户登陆的权限验证就是具体要执行的业务,在SpringAOP中,可以在方法上加以下注解,该方法就会变为通知方法,在满足条件后就会被调用

1.前置通知使⽤ @Before

通知方法在目标方法(就是连接点)执行之前调用

    @Before("pointcut()")public void doBefore() {log.info("doBefore...");}

此时我们在浏览器去搜索(假装登陆操作)
在这里插入图片描述
我们查看日志就可以发现在登陆操作之前,执行了Before注解的方法
在这里插入图片描述

2.后置通知使⽤ @After

该方法会在连接点(也就是目标方法)返回之后调用执行,或者抛出异常之后也会调用

    @After("pointcut()")public void doAfter() {log.info("doAfter...");}

此时去浏览器搜索之后发现在登陆操作之后,执行了@After注解的方法
在这里插入图片描述

3.返回之后通知使⽤ @AfterReturning

这个通知方法和@After都实在目标方法返回之后才调用,那么这两个的先后执行顺序是怎么样的呢?
我们来测试一下

    @AfterReturning("pointcut()")public void doAfterReturning() {log.info("doAfterReturning...");}

在浏览器操作后,查看日志发现@doAfterRuturning注解的方法比@After注解的方法先执行,但是@After还可以在抛出异常时调用,而且一般这两个不会同时使用
在这里插入图片描述

4.抛异常后通知使⽤ @AfterThrowing

该方法会在连接点抛出异常后调用
这个方法和@After注解,都拥有这个功能,那么这两个方法执行的先后顺序如何呢?
我们来测试一下

    @AfterThrowing("pointcut()")public void doAfterThrowing() {log.info("doAfterThrowing...");}

我们查看日志发现,@AfterThrowing同样是比@After注解的方法先执行
在这里插入图片描述

5.环绕通知使⽤ @Around(使用最多)

该方法包裹了连接点(也就是目标方法),在连接点通知之前和调用之后执行自定义的行为
注意:环绕通知的返回结果需要自己定义

    @Around("pointcut()")public Object doAround(ProceedingJoinPoint joinPoint) {// 传入当前的连接点// 定义返回结果Object oj = null;log.info("环绕通知执行之前...");try {// 调用目标方法oj = joinPoint.proceed();} catch (Throwable e) {throw new RuntimeException(e);}log.info("环绕通知执行之后...");return oj;}

此时我们查看日志,可以看见在连接点通知之前和调用之后都执行了环绕通知自定义的行为
在这里插入图片描述

6.实际应用

这样我们就可以在使用切点制定一组规则,(设置连接点)拦截需要验证身份的方法,使得他们在执行前都调用一下身份验证,就像这样:

    @Around("pointcut()")public Object VerifyIdentity(ProceedingJoinPoint joinPoint) {Object oj = null;log.info("身份验证的方法");try {// 执行目标方法oj = joinPoint.proceed();} catch (Throwable e) {throw new RuntimeException(e);}return oj;}

我们在浏览器分别操作,登录,注册,获取信息的场景
查看日志发现在每一次操作之前都调用了身份验证的方法,
在这里插入图片描述
这样我们就实现了使用AOP进行统一的用户判断,是不是方便了许多呢?
本篇博客就到这里啦!我们还是下篇博客见~~

在这里插入图片描述

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

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

相关文章

git实战

git实战 第一章 快速入门 1.1 什么是git git是一个分布式的版本控制软件。 软件&#xff0c;类似于QQ、office、dota等安装到电脑上才能使用的工具。版本控制&#xff0c;类似于毕业论文、写文案、视频剪辑等&#xff0c;需要反复修改和保留原历史数据。分布式 - 文件夹拷贝…

rk3588 编译(使用github公版源码)和烧录(sd、emmc)

使用 github 上的代码进行编译 https://github.com/rockchip-linux/rkbin/tree/master https://github.com/rockchip-linux/u-boot/tree/next-dev https://github.com/rockchip-linux/kernel/tree/develop-5.10 下载他们的压缩包&#xff0c;自行解压&#xff0c;解压后把文件夹…

RocketMQ教程-(4)-领域模型概述

Apache RocketMQ 是一款典型的分布式架构下的中间件产品&#xff0c;使用异步通信方式和发布订阅的消息传输模型。通信方式和传输模型的具体说明&#xff0c;请参见下文通信方式介绍和消息传输模型介绍。 Apache RocketMQ 产品具备异步通信的优势&#xff0c;系统拓扑简单、上下…

Java-IDEA好用的插件

Lombok&#xff0c;结合一些列注解&#xff0c;帮我们轻松解决重复编写实体类get、set、toString、build、构造方法等麻烦 Chinesepinyin-CodeComp&#xff0c;让界面汉化&#xff0c;使用起来更有亲和力 MyBatisX,点击小鸟图标&#xff0c;轻松再Mapper接口与xml文件之间实…

无涯教程-html(val)

html(val)方法设置每个匹配元素的html内容。此属性在XML文档上不可用&#xff0c;但适用于XHTML文档。 html( val ) - 语法 selector.html( val ) 这是此方法使用的所有参数的描述- val - 任何字符串 html( val ) - 示例 以下示例将获取第一段的HTML内容&#xff0c;并…

【算法与数据结构】104、111、LeetCode二叉树的最大/最小深度

文章目录 一、题目二、层序遍历法三、递归法四、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、层序遍历法 思路分析&#xff1a;两道题都可以用层序遍历&#xff08;迭代法&#xff09;来做&#xff0c;遍历完…

帮助中心内容需要囊括什么?(内含案例分享)

给产品制作一个帮助中心&#xff0c;让用户能够通过访问帮助中心查看产品相关内容&#xff0c;尽快了解产品&#xff0c;熟悉操作。不仅仅局限于售后&#xff0c;在售中售前都能够发挥很大的作用&#xff0c;帮助用户全面了解产品&#xff0c;减少销售的工作量&#xff0c;节约…

如何开展企业级spring cloud微服务开发实践落地

随着互联网技术的不断发展&#xff0c;微服务架构已经成为了企业级应用开发的重要趋势。Spring Cloud是基于Spring Boot的微服务框架&#xff0c;它提供了一系列的组件和工具&#xff0c;可以帮助开发者快速搭建、部署和管理微服务系统。本文将介绍如何开展企业级Spring Cloud微…

Zookeeper命令总结

目录 1、常用命令2、ls path3、create xxx创建持久化节点创建临时节点创建持久化序列节点 4、get path5、set path6、delete path7、监听器总结1&#xff09;节点的值变化监听2&#xff09;节点的子节点变化监听&#xff08;路径变化&#xff09;3&#xff09;当某个节点创建或…

最优化方法

一. 图论 1.最小生成树 图的生成树是它的一颗含有其所有顶点的无环连通子图,一 幅加权图的最小生成树(MST)是它的一颗权值(树中的所有边的权值之和) 最小的生成树 • 适用场景&#xff1a;道路规划、通讯网络规划、管道铺设、电线布设等 题目数据 kruskal算法 稀疏图&#x…

oracle单个用户最大连接数限制

项目经理反馈&#xff0c;现场已做了单个用户的最大连接数2000的限制&#xff0c;但数据库还是报无法连接&#xff0c;故障用户的连接数已3800多了。 查看日志报错如下 2023-07-20T13:07:57.79465308:00 Process m000 submission failed with error 20 Process m000 submiss…

HDFS的设计目标和重要特性

HDFS的设计目标和重要特性 设计目标HDFS重要特性主从架构分块存储机制副本机制namespace元数据管理数据块存储 设计目标 硬件故障(Hardware Failure)是常态&#xff0c;HDFS可能有成百上千的服务器组成&#xff0c;每一个组件都有可能出现故障。因此古见检测和自动快速恢复的H…

梯度提升树的基本思想

目录 1. 梯度提升树 VS AdaBoost 2. GradientBoosting回归与分类的实现 2.1 GradientBoosting回归 2.2 GradientBoosting分类 1. 梯度提升树 VS AdaBoost 梯度提升树&#xff08;Gradient Boosting Decision Tree&#xff0c;GBDT&#xff09;是提升法中的代表性算法&#…

赛码-0726

01串的魔法 思路&#xff1a;虽然标着dp&#xff0c;其实是滑动窗口问题&#xff0c;dp 会超时 import java.util.*;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt();int k scanner.nextInt…

帆软报表设计器设置步骤

1、连接工作目录&#xff08;可以是远程服务器&#xff09; 在打开的界面中设置具体的远程地址 一个报表文件可以有多个数据集、但是数据集依附于报表文件的存在&#xff0c;不能跨报表共享。 先补充这么多&#xff0c;有遇到问题再写一点。 &#xff08;完&#xff09;

指针易混淆概念:*p++,*(p++),++(*p)

&#xff08;1&#xff09;a[1]和&a[1] a[1]:二维数组的第二行元素&#xff0c;即a[1]是以a[1][0]开头的一维数组&#xff0c;类型是一级指针 &a[1]:二维数组的第二行元素的地址&#xff0c;类型是二级指针 &#xff08;2&#xff09;a1与*&#xff08;a1&#xff09…

我的创作纪念日——暨成为创作者满1024日的总结

我的创作纪念日 机缘收获日常成就憧憬 机缘 最初写博客&#xff0c;是因为身边一个同事&#xff0c;写了几篇博客&#xff0c;然后给我臭摆&#xff0c;于是&#xff0c;不服输的自己也动手开始写了。之后&#xff0c;就逐渐写出惯性来了&#xff0c;现在每月4篇&#xff0c;基…

Cpp 01 — namespace命名空间、C++的输入与输出、缺省参数、函数重载、引用、隐式类型转换

前言&#xff1a;本文章主要用于个人复习&#xff0c;追求简洁&#xff0c;感谢大家的参考、交流和搬运&#xff0c;后续可能会继续修改和完善。 因为是个人复习&#xff0c;会有部分压缩和省略。 一、namespace命名空间 C使用命名空间(namespace)来避免命名冲突。 在定义一个…

最小生成树

最小生成树 1.朴素Primm~n^2稠密图 Part1:例题 给定一个 n 个点 m 条边的无向图&#xff0c;图中可能存在重边和自环&#xff0c;边权可能为负数。 求最小生成树的树边权重之和&#xff0c;如果最小生成树不存在则输出 impossible。 数据范围&#xff1a; 1≤n≤5001≤n≤…

STM32CUBUMX配置RS485(中断接收)--保姆级教程

———————————————————————————————————— ⏩ 大家好哇&#xff01;我是小光&#xff0c;嵌入式爱好者&#xff0c;一个想要成为系统架构师的大三学生。 ⏩最近在开发一个STM32H723ZGT6的板子&#xff0c;使用STM32CUBEMX做了很多驱动&#x…