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

news2024/11/23 19:57:33

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.coloradmin.cn/o/792471.html

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

相关文章

PCIe Error Signaling and Logging持续更新

来源PCI5.0 SPEC 1.错误报告范式ERROR REPORTING PARADIGMS PCI Express定义了两种错误报告范式&#xff1a;baseline capability and the Advanced Error Reporting Capability。baseline错误报告能力是所有PCI Express设备都需要具备的&#xff0c;它定义了最低限度的错误报…

基于Ubuntu 22.04 编译chip-tool工具

前言 编译过程有点曲折&#xff0c;做下记录&#xff0c;过程中&#xff0c;有参考别人写的博客&#xff0c;也看github 官方介绍&#xff0c;终于跑通了~ 环境说明&#xff1a; 首先需要稳定的梯子&#xff0c;可以访问“外网”ubuntu 环境&#xff0c;最终成功实验在Ubunt…

Games101学习笔记 - 基础数学

向量 向量&#xff1a;方向和长度&#xff0c;没有起始位置 向量长度&#xff1a;各个方向平方相加开方 单位向量&#xff1a;向量除向量的长度 点乘 在笛卡尔坐标系中的点乘计算&#xff1a; 几何意思&#xff1a; 表示一个向量在另一个向量上的投影点乘在图形学中应用&a…

SQLite Studio 连接 SQLite数据库

1、在SQLite中创建数据库和表 1.1、按WINR&#xff0c;打开控制台&#xff0c;然后把指引到我们的SQLite的安装路径&#xff0c;输入D:&#xff0c;切换到D盘&#xff0c;cd 地址&#xff0c;切换到具体文件夹&#xff0c;输入“sqlite3”&#xff0c;启动服务 1.2、创建数据库…

最全SWAT教程:SWAT模型系统学习(建模方法、实例应用、高级进阶)

目前&#xff0c;水环境问题逐渐成为制约社会经济和环境可持续发展的重要因素。根据国内外研究表明&#xff0c;受全球环境变化和经济快速发展的影响&#xff0c;面源污染已逐渐成为水环境污染的第一因素。但面源污染由于具有排放分散、隐蔽&#xff0c;排污随机、不确定、不易…

关于封装的定义?以及API接口封装作用有哪些

封装是面向对象编程中的一个重要概念&#xff0c;它指的是将数据和程序代码包含在类中&#xff0c;并对外部对象隐藏其内部实现细节&#xff0c;只提供公共接口。这种方式可以有效地保护数据&#xff0c;防止被外部对象随意访问或修改&#xff0c;同时也更容易维护、升级和复用…

Linux下top命令用法详解

一、命令介绍 Linux top命令用于实时显示 process &#xff08;进程&#xff09;的动态。它用于监控正在运行系统负荷的信息&#xff0c;包括系统负载、CPU利用分布情况、内存使用、每个进程的资源占用情况等。 使用权限&#xff1a;所有使用者 二、命令详解 在命令行下输入…

串稳定混合交通的协同自适应巡航控制:基准和以人为本的设计(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 串稳定混合交通的协同自适应巡航控制是一种针对复杂交通环境的控制方法&#xff0c;旨在实现交通系统的高效运行和安全性。其中…

ATTO488 NHS ester ,新型亲水性荧光标记物,具有良好的水溶性

陕西新研博美生物科技有限公司MISS.wu小编&#xff08;2023.7月26日&#xff09;为大家整理以下的内容&#xff1a; Atto488-NHS是一种新型亲水性荧光标记物&#xff0c;具有良好的水溶性。这种染料表现得很浓吸收、高荧光量子产率以及优异的热稳定性和光稳定性。因此&#xff…

推送docker镜像到私有/DockerHub仓库

制作本地镜像 以定制jdk11镜像为例&#xff1a; 新建文件夹jdk11下载orcale jdk11&#xff0c;复制orcale jdk11 到刚刚新建文件夹jdk11下新建Dockerfile&#xff0c;镜像对字符集和时间都做了处理 FROM centos:7 MAINTAINER cc <ccqq.com>ADD jdk-11.0.10_linux-x64_…

多线程——多线程的创建方式、常用成员方法

目录 一、什么是多线程&#xff1f; 线程与进程 进程与线程区别&#xff1a; 并发与并行 二、多线程的创建方式&#xff08;三种方式&#xff09; 第一种&#xff1a;继承Thread类方式创建 第二种&#xff1a;实现Runnable接口的方式创建 第三种&#xff1a;利用Callab…

uniapp实现预约时间选择弹窗组件

做了个组件&#xff0c;实现出当日预约时间组件&#xff0c;效果图如下 废话不多说&#xff0c;直接上代码&#xff0c;代码简单&#xff0c;参数自己任意改 <template><view class"inventory"><u-popup :show"show" :round"10"…

全国青少年信息素养大赛Scratch图形化编程_初赛_模拟二卷

全国青少年电子信息智能创新大赛Scratch图形化编程_初赛_模拟二卷 一、选择题 第 1 题 单选题 执行下面程序&#xff0c;角色会说&#xff1f;&#xff08; &#xff09; A.34 B.28 C.51 D.42 第 2 题 单选题 默认小猫角色&#xff0c;初始位置在舞台中心&#xff0c;下面…

Vuex模块化管理

如果你的项目是一个小型项目&#xff0c;就用不着使用模块化&#xff1b; 但是&#xff0c;如果你参与的项目是一个中大型项目&#xff0c;那Vuex模块化&#xff0c;必不可少&#xff0c;否则整个文件很臃肿&#xff0c;也很难管理。 通过模块化管理&#xff1a;各自模块下都有…

【Linux环境搭建篇】--- 还不会搭建Linux环境?收藏这一篇就够了

文章目录 前言&#x1f31f;一、Linux背景介绍&#x1f30f;1.1. 发展史&#x1f30f;1.2. 开源 &#x1f31f;二、搭建Linux环境&#x1f31f;三、使用 XShell 远程登陆到 Linux&#x1f31f;四、XShell 下的复制粘贴&#x1f63d;总结 前言 &#x1f467;个人主页&#xff1a…

AF647 Azide,Alexa Fluor 647 Azide,明亮且光稳定的荧光基团

文章编辑来自于&#xff1a;陕西新研博美生物科技有限公司MISS.wu​ Alexa Fluor 647 Azide&#xff0c;Alexa Fluor 647 N3&#xff0c;AF647 Azide&#xff0c;AF 647 N3| AF 647 叠氮&#xff0c;Alexa Fluor 647叠氮 | CAS&#xff1a;N/A | 纯度&#xff1a;95% PA…

vue项目加按钮,两个不同的项目当前页进行互相切换

服务器上两个项目当前页互相切换 直接使用 window.location.href 调整的页面地址 这里一定要注意服务器上项目的前缀名称即可。

【iOS】—— RunLoop和多线程相关问题总结

RunLoop 1. 讲讲RunLoop&#xff0c;项目中有用到过吗&#xff1f; RunLoop 的基本作用&#xff1a;保持程序的持续运行&#xff0c;节省 CPU 的资源&#xff0c;提高程序的性能 &#xff08; 没有事情&#xff0c;就请休眠&#xff0c;不要功耗。有事情&#xff0c;就处理&a…

如何制定数据采集解决方案?

数据采集仍是人工智能&#xff08;AI&#xff09;构建团队的主要瓶颈。原因各不相同&#xff1a;用例数据可能不足&#xff0c;深度学习等新机器学习&#xff08;ML&#xff09;技术需要更多数据&#xff0c;或者团队并未建立获取所需数据的适当流程。但无论如何&#xff0c;对…

xmind latex【记录备忘】

xmind latex 换行 换行必须要有\begin{align}和\end{align}&#xff0c;此时再在里面用\才能换行&#xff0c;如果只写112\224是不能换行的