当前位置: 首页 > news >正文

SpringAOP面向切面编程

文章目录

  • 一. AOP是什么?
  • 二. AOP相关概念
  • 三. SpringAOP的简单演示
  • 四. SpringAOP实现原理

一. AOP是什么?

AOP(Aspect Oriented Programming):面向切面编程,它是一种编程思想,是对某一类事情的集中处理它能够在不改原有代码的前提下对其功能进行增强,就是你代码已经写好了,使用 AOP 可以在不改动代码的前提下增强功能,如对于一个功能,可以基于 AOP 完成对该功能执行效率的计算,能够在功能正式执行前或者执行后,添加其他的功能执行,能够在该功能发生异常后,对其异常进行处理。

想象一个场景,我们在做后台系统时,除了登录和注册等几个功能不需要做用户登录验证之外,其他几乎所有页面都需要先验证用户登录的状态,那这个时候我们要怎么处理呢?

如果不使用 AOP,我们就需要在每一个 Controller 层都写一遍验证用户是否已经登录的逻辑,如果你实现的功能有很多,并且这些功能都需要进行登录验证,那你就需要编写大量重复的代码, 这样代码修改和维护的成本也会很高。

但如果使用 AOP,在进入核心的业务代码之前会做统一的一个拦截,去验证用户是否登录,验证通过的就可以继续请求,此时就不需要每一处都写相同的用户登录逻辑了。

除了登录验证功能之外,还有很多功能也可以使用 AOP,比如:

  • 统一日志记录与持久化。
  • 统一方法执行时间统计。
  • 统一数据返回格式。
  • 统一处理程序中的异常。
  • 统一事务的开启与提交。

也就是说使用 AOP 可以扩充多个对象的某个能力,所以 AOP 可以说是 OOP (Object Oriented Programming,面向对象编程)的补充和完善,它可以将横切关注点从应用程序的主业务逻辑中分离出来,使得这些关注点可以集中处理,从而提高代码复用性、可维护性和系统可扩展性。

SpringAOP 是一个框架,提供了对 AOP 的实现,与 IOC 与 DI 的关系类似。

二. AOP相关概念

  1. 切面(Aspect)–> 类:某一方面的具体内容处理(AOP实现的某个功能的集合)就是一个切面,由切点(Pointcut)和通知(Advice)组成 ;比如用户登录判断就是一个切面(接口对于登录权限的校验)。
  2. 切点(Pointcut)–>方法:定义拦截规则;比如切面对于哪些接口需要进行判断拦截。
  3. 连接点(Joinpoint):所有可能触发切点的点就是连接点(被这个切面所处理的点)。
  4. 通知(方法的具体实现):执行 AO P业务(具体需要执行的拦截方法)。

在 Spring 切面类中,可以在方法上使用以下注解,会设置方法为通知方法,在满足条件后会调用对应满足条件的方法:

  • 前置通知使用 @Before∶通知方法会在目标方法调用之前执行。
  • 后置通知使用 @After∶通知方法会在目标方法返回或者抛出异常后调用。
  • 返回之后通知使用 @AfterReturning∶ 通知方法会在目标方法返回后调用。
  • 抛异常后通知使用 @AfterThrowing∶ 通知方法会在目标方法抛出异常后调用。
  • 环绕通知使用 @Around∶通知包裹了被通知的方法,在被通知的方法通知之前和调用之后执行自定义的行为。

AOP 整个组成部分的概念如下图所示,以多个⻚⾯都要访问⽤户登录权限为例:

img

三. SpringAOP的简单演示

1️⃣第一步,添加 Spring Boot AOP 依赖支持。

在 SpringBoot 项目中导入 AOP 依赖时可以不设置版本号,SpringBoot 会帮助我们自动适配。

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

2️⃣第二步,定义切面,我们使用@Aspect注解将类标识为切面类,并使用注解 @Component 将类实例化到容器中。

package com.example.demo.common;import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect // 定义切面
@Component
public class UserAspect {}

3️⃣第三步,定义切点,配置拦截规则,可以使用AspectJ表达式类来进行描述,它的语法格式如下:

切入点表达式标准格式:动作关键字(访问修饰符  返回值  包名./接口名.方法名(参数) 异常名)

AspectJ ⽀持三种通配符:

  • *:匹配任意字符,只匹配⼀个元素(包,类,或⽅法,⽅法参数)。

  • .. :匹配任意字符,可以匹配多个元素 ,在表示类时,必须和*联合使⽤。

  • +:表示按照类型匹配指定类的所有类,必须跟在类名后⾯,如com.cad.Car+,表示继承该类的
    所有⼦类包括本身。

其中访问修饰符和异常可以省略,比如下面这个表达式:

img

任意声明一个方法,不需要具体实现,使用注解@Pointcut修饰,里面的value属性填写AspectJ表达式,这个方法就可以视为连接目标方法的一个切点。

但要注意,这种表达式的书写是非常繁琐的,目前有更好的AOP实现,可以更加灵活的配置,也就是说这里的写法其实并不常用。

代码实现:

@Aspect // 定义切面
@Component
public class UserAspect {// 切点@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")public void pointcut() {}
}

4️⃣第四步,创建通知。

有了切点,要对切点处的方法进行相关处理,需要编写具体的增强方法,也就是通知,下面我们需要在切面类中简单编写几个方法来表示通知,通知就是将共性功能抽取出来后形成的方法。

要注意对于环绕通知,由于在实现增强方法时,需要介入到方法执行前和后,那么必须得获取到目标的方法,不然无法控制你实现的那些代码是在目标方法执行前执行的,哪些方法是在目标方法执行后执行的。

对于这项工作,SpringAOP 已经帮我们做了,所有的切点方法都已经加载到ProceedingJoinPoint对象当中,只要调用proceed方法就能够执行目标方法。

package com.example.demo.common;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Aspect // 定义切面
@Component
public class UserAspect {// 切点@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")public void pointcut() {}// 前置通知通知@Before("pointcut()")public void doBefore() {System.out.println("执行了前置通知");}// 后置通知@After("pointcut()")public void doAfter() {System.out.println("执行了后置通知");}// 环绕通知@Around("pointcut()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("环绕通知执行之前");// 执行目标方法Object result = joinPoint.proceed();System.out.println("环绕通知执行之后");return result;}
}

5️⃣第五步,创建连接点。

package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@RequestMapping("/getuser")public String getUser(){System.out.println("do getUser");return "get user";}@RequestMapping("/deluser")public String delUser(){System.out.println("do delUser");return "del user";}
}

🍂此时,就可以启动 SpringAOP 了,然后通过浏览器去访问定义的这两个连接点,看通知效果。

img

img

🍂我们可以在Controller层再创建一个ArticleController类用来对照,访问一下看此时是否会执行通知。

package com.example.demo.controller;import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/art")
public class ArticleController {@RequestMapping("/getart")public String getArticle(){System.out.println("do getArticle");return "getArticle";}
}

结果如下:

因为我们设置的拦截规则不包含这个类,也就不会执行通知了。

img

img

四. SpringAOP实现原理

Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的支持局限于方法级别的拦截(使用动态代理技术实现方法的调用)。

Spring AOP 支持 JDK ProxyCGLIB 方式实现动态代理。默认情况下,实现了接口的类,使用 SpringAOP 会基于 JDK 生成代理类,没有实现接口的类,会基于 CGLIB 生成代理类。

img

下面简单的来说一下原理,首先,要有一个目标对象,在上面的例子中,目标对象就是UserController,然后通过基于这个目标对象,创建一个代理类,并在某一规定的时机生成,这个时机就是织入,最后在这个代理类上加上一些增强的方法,这个过程就叫做引入

🎯下面又出来了一组相关概念:

目标对象:代理的目标对象。

织入(weaving): 即代理的生成时机,织入是把切面应用到目标对象并创建新的代理对象的过程,切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入∶

  • 编译期∶切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ 的织入编译器就是以这种方式织入切面的。
  • 类加载器∶切面在目标类加载到 JVM 时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5 的加载时织入(load-time weaving.LTW)就支持以这种方式织入切面。
  • 运行期∶切面在应用运行的某一时刻被织入。一般情况下,在织入切面时,AOP 容器会为目标对象动态创建一个代理对象。SpringAOP 就是以这种方式织入切面的。

引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。

🍂JDK Proxy 与 CGLIB 的区别:

  1. 出身不同,一个由 JDK 实现,一个由 CGLIB 实现。
  2. 实现不同,JDK Proxy 要求代理类实现接口才能实现代理,它只能代理接口中定义的方法;CGLIB 是通过实现代理类的子类完成动态代理,可以为任意一个类创建代理对象,包括没有实现任何接口的类,它能够代理类中所有非 final 的方法。
  3. 性能不同,JDK7 之后 JDK Proxy 性能是略高于 CGLIB 的; 而在 JDK7 之前 CGLIB 性能是略高于 JDK Proxy 的。

相关文章:

SpringAOP面向切面编程

文章目录 一. AOP是什么&#xff1f;二. AOP相关概念三. SpringAOP的简单演示四. SpringAOP实现原理 一. AOP是什么&#xff1f; AOP&#xff08;Aspect Oriented Programming&#xff09;&#xff1a;面向切面编程&#xff0c;它是一种编程思想&#xff0c;是对某一类事情的集…...

A Guide to Java HashMap

原文链接: A Guide to Java HashMap → https://www.baeldung.com/java-hashmap 从Map里取值 # 原生方法 Map<String, Integer> map new HashMap<>();// map自身的方法 → 取不到返回null Integer age6 map.get("name"); // Integer时返回null可…...

LeetCode 449. Serialize and Deserialize BST【树,BFS,DFS,栈】困难

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

嵌入式IDE(1):IAR中ICF链接文件详解和实例分析

最近在使用NXP的提供的MCUXPresso IDE&#xff0c;除了Eclipse固有的优点外&#xff0c;我觉得它最大的优点就是在链接脚本的生成上&#xff0c;提供了非常直观的GUI配置界面。但这个IDE仅仅支持NXP相关的产品&#xff0c;而且调试的性能在某些情况下并不理想。而我们用得比较多…...

分布式版本控制工具——git

✅<1>主页&#xff1a;&#xff1a;我的代码爱吃辣 &#x1f4c3;<2>知识讲解&#xff1a;Linux——git ☂️<3>开发环境&#xff1a;Centos7 &#x1f4ac;<4>前言&#xff1a;git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很…...

C基础-数组

1.一维数组的创建和初始化 int main() {// int arr1[10];int n 0;scanf("%d",&n);//int count 10;int arr2[n]; //局部的变量&#xff0c;这些局部的变量或者数组是存放在栈区的&#xff0c;存放在栈区上的数组&#xff0c;如果不初始化的话&#xff0c;默认…...

springboot项目配置flyway菜鸟级别教程

1、Flyway的工作原理 Flyway在第一次执行时&#xff0c;会创建一个默认名为flyway_schema_history的历史记录表&#xff0c;这张表会用来跟踪或记录数据库的状态&#xff0c;然后每次项目启动时都会自动扫描在resources/db/migration下的文件的版本号并且通过查询flyway_schem…...

成都精灵云初试

最近参加了成都精灵云的笔试与面试&#xff0c;岗位是c工程师。后面自己复盘了过程&#xff0c;初试部分总结如下&#xff0c;希望能对各位相进该公司以及面试C工程师的同学提供一些参考。这也是博主第一次参加面试&#xff0c;很多东西都还没准备&#xff0c;很多答得不好&…...

css relative 和absolute布局

1、relative和absolute内部的元素都是相对于父容器&#xff0c;若父容器没有指定为relative&#xff0c;则默认为整个文档视图空间&#xff0c;absolute可以重叠元素&#xff0c;relative则不行。relative意味着元素的任意属性如left和right都是相对于其他元素的。absolute则相…...

更健康舒适更科技的照明体验!书客SKY护眼台灯SUKER L1上手体验

低价又好用的护眼台灯是多数人的需求&#xff0c;很多人只追求功能性护眼台灯&#xff0c;显色高、无频闪、无蓝光等基础需求。但是在较低价格中很难面面俱到&#xff0c;然而刚发布的SUKER书客L1护眼台灯却是一款不可多得的性价比护眼台灯&#xff0c;拥有高品质光源&#xff…...

经管博士科研基础【19】齐次线性方程组

1. 线性方程组 2. 非线性方程组 非线性方程,就是因变量与自变量之间的关系不是线性的关系,这类方程很多,例如平方关系、对数关系、指数关系、三角函数关系等等。求解此类方程往往很难得到精确解,经常需要求近似解问题。相应的求近似解的方法也逐渐得到大家的重视。 3. 线…...

django报错解决 Forbidden (403) CSRF verification failed. Request aborted.

django报错解决 Forbidden (403) CSRF verification failed. Request aborted. 报错内容 Forbidden (403) CSRF verification failed. Request aborted.Help Reason given for failure:Origin checking failed - https://active-mantis-distinct.ngrok-free.app does not mat…...

k8s-实战——yapi平台部署

文章目录 k8s 部署yapi平台前言准备工作构建yapi镜像Dockerfileentrypoint.shbuild.sh源码下载构建镜像启动mongo数据库新建nfs服务mongo创建mongo服务初始化数据启动yapi服务创建yapi服务查看密码访问地址k8s 部署yapi平台 前言 部署yapi平台需要mo...

Excel VSTO开发5 -Excel对象结构

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 5 Excel对象结构 Excel提供了几个比较重要的对象&#xff1a; Application、Workbooks、Workbook、Worksheets、Worksheet 为了便…...

Javafx集成sqlite数据库

什么是SQLite SQLite是一款非常轻量级的关系数据库系统&#xff0c;支持多数SQL92标准。SQLite在使用前不需要安装设置&#xff0c;不需要进程来启动、停止或配置&#xff0c;而其他大多数SQL数据库引擎是作为一个单独的服务器进程&#xff0c;被程序使用某种内部进程通信(典型…...

react-native实现 TextInput 键盘显示搜索按钮并触发回调

<TextInput returnKeyType"search"returnKeyLabel"搜索"onSubmitEditing{e > {toSearch(keyword);}} /><SearchBarref{serachBarEl}placeholder"请输入"onChangeText{handleChangeSearch}value{search}onSubmitEditing{handleSearch…...

人大金仓分析型数据库备份和恢复(五)

增量备份 gpbackup和gprestore工具支持创建追加优化表的增量备份以及从增量备份还原。 只有表被更改时&#xff0c;增量备份才会备份所有指定的堆表和追加优化的表&#xff08;包括追加优化的&#xff0c;面向列的表&#xff09;。 例如&#xff0c;如果追加优化表的行已更改&a…...

lenovo联想笔记本ThinkPad P16V Gen 1(21FC,21FD)原装出厂Win11系统

原厂W11系统自带所有驱动、出厂主题壁纸、Office办公软件、联想电脑管家等预装程序 链接&#xff1a;https://pan.baidu.com/s/17dTExDSz-EDN4Qd-PZGJuw?pwdrgl3 提取码&#xff1a;rgl3 所需要工具&#xff1a;32G或以上的U盘 文件格式&#xff1a;ISO 文件大小…...

Django实现音乐网站 ⒃

使用Python Django框架制作一个音乐网站&#xff0c; 本篇主要是歌手详情页-专辑列表、专辑详情-单曲列表开发实现内容。 目录 歌手详情-专辑列表 路由设置 跳转设置 视图方法 模板内容 专辑详情-单曲列表 设置路由 视图处理并返回 模板渲染 分页优化 引入错误类型库…...

【开发问题系列】CSV转Excel

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

ETLCloud可能遇到的问题有哪些?常见坑位解析

数据集成平台ETLCloud&#xff0c;主要用于支持数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;过程。提供了一个简洁直观的界面&#xff0c;以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...