详解Spring AOP(一)

目录

1. AOP概述

2.Spring AOP快速入门

2.1引入AOP依赖

2.2编写AOP程序

 3.Spring AOP核心概念

3.1切点(PointCut)

3.2连接点(Join Point)

3.3通知(Advice)

3.4切面(Aspect)

4.通知类型

 5.@PointCut

 6.切面优先级 @Order


1. AOP概述

AOP(Aspect Oriented Programming,面向切面编程)是一种编程范式,它旨在通过预编译方式和运行期间动态代理实现程序功能的统一维护。

AOP定义

  1. AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点技术。
  2. 它是Spring框架中的一个重要内容,也是函数式编程的一种衍生范型。
  3. AOP通过“切面”对业务逻辑的各个部分进行隔离,降低业务逻辑之间的耦合度,提高程序的可重用性和开发效率。

 与OOP的区别:

OOP针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

应用场景:记录日志、性能监控、权限控制、缓存优化、事务管理(如声明式事务)

案例分析:

我们现在有⼀个项⽬ , 项⽬中开发了很多的业务功能

现在有⼀些业务的执行效率比较低, 耗时较长, 我们需要对接口进行优化.

第⼀步就需要定位出执行耗时比较长的业务方法, 再针对该业务方法来进行优化

何定位呢? 我们就需要统计当前项目中每⼀个业务方法的执行耗时.

如何统计呢? 可以在业务方法运行前和运行后, 记录下方法的开始时间和结束时间, 两者之差就是这个方法的耗时.

这种方法是可以解决问题的, 但⼀个项⽬中会包含很多业务模块, 每个业务模块又有很多接口 , ⼀个接口又包含很多方法, 如果我们要在每个业务方法中都记录方法的耗时, 会增加特别多的工作.

AOP就可以做到在不改动这些原始方法的基础上, 针对特定的方法进⾏功能的增强.

AOP的作用:在程序运行期间在不修改源代码的基础对已有方法进行增强(无侵⼊性: 解耦)

2.Spring AOP快速入门

需求:统计系统各个方法的zhixing

2.1引入AOP依赖

pom.xml文件中添加配置

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

2.2编写AOP程序

记录Controller中每个方法的执行时间

 @Slf4j@Aspect@Componentpublic class TimeAspect {/*** 记录⽅法耗时 */@Around("execution(* com.example.demo.controller.*.*(..))")public Object recordTime(ProceedingJoinPoint pjp) throws Throwable { //记录⽅法执⾏开始时间long begin = System.currentTimeMillis(); //执⾏原始⽅法Object result = pjp.proceed(); //记录⽅法执⾏结束时间long end = System.currentTimeMillis(); //记录⽅法执⾏耗时log.info(pjp.getSignature() + "执⾏耗时 : {}ms", end - begin);return result;}}

运行程序, 观察日志

 对代码进行简单分析:

1.@Aspect:标识这是一个切面类
2.@Around:环绕通知,在目标方法的前后都会被执行.后面的表达式表示对哪些方法进行增强
3.ProceedingJoinPoint.proceed()让原始方法执行

整个代码划分为三部分 

我们通过AOP入门程序完成了业务接口执行耗时的统计.

通过上面的程序,我们也可以感受到AOP面向切面编程的一些优势:

  • 代码无侵入:不修改原始的业务方法,就可以对其进行了功能的增强或者是功能的改变
  • 减少了重复代码
  • 提高开发效率
  • 维护方便

 3.Spring AOP核心概念

3.1切点(PointCut)

切点(Pointcut),也称之为"切入点"
切点的作用就是提供一组规则(使用AspectJ pointcut expression language来描述),告诉程序对哪些方法来进行功能增强.

上面的表达式execution(* com.example.demo.controller.*.*(..))就是切点表达式. 

3.2连接点(Join Point)

满足切点表达式规则的方法,就是连接点.也就是可以被AOP控制的方法以入门程序举例,所有com.example.demo.controller路径下的方法,都是连接点.

 package com.example.demo.controller; @RequestMapping("/book") @RestControllerpublic class BookController { @RequestMapping("/addBook")public Result addBook(BookInfo bookInfo) {//...代码省略} @RequestMapping("/queryBookById")public BookInfo queryBookById(Integer bookId){//...代码省略} @RequestMapping("/updateBook")public Result updateBook(BookInfo bookInfo) {//...代码省略}}

上述BookController中的方法都是连接点

切点和连接点的关系

连接点是满足切点表达式的元素.切点可以看做是保存了众多连接点的一个集合

比如:

切点表达式:学校全体教师

连接点就是:张三,李四等各个老师

3.3通知(Advice)

通知就是具体要做的工作,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)比如上述程序中记录业务方法的耗时时间,就是通知.

在AOP面向切面编程当中,我们把这部分重复的代码逻辑抽取出来单独定义,这部分代码就是通知的内容.

3.4切面(Aspect)

切面(Aspect)=切点(Pointcut)+通知(Advice)

通过切面就能够描述当前AOP程序需要针对于哪些方法,在什么时候执行什么样的操作.

切面既包含了通知逻辑的定义,也包括了连接点的定义.

 切面所在的类,我们一般称为切面类(被@Aspect注解标识的类)

4.通知类型

上面我们讲了什么是通知,接下来学习通知的类型.

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

@Around:环绕通知,此注解标注的通知方法在目标方法前,后都被执行
@Before:前置通知,此注解标注的通知方法在目标方法前被执行
@After:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行
@AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不执行
@AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行

 通过代码来测试这几个通知:

import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;@Slf4j@Aspect@Componentpublic class AspectDemo { //前置通知@Before("execution(* com.example.demo.controller.*.*(..))")public void doBefore() {log.info("执⾏  Before ⽅法");} //后置通知@After("execution(* com.example.demo.controller.*.*(..))")public void doAfter() {log.info("执⾏  After ⽅法");} //返回后通知@AfterReturning("execution(* com.example.demo.controller.*.*(..))")public void doAfterReturning() {log.info("执⾏  AfterReturning ⽅法");} //抛出异常后通知@AfterThrowing("execution(* com.example.demo.controller.*.*(..))")public void doAfterThrowing() {log.info("执⾏  doAfterThrowing ⽅法");} //添加环绕通知@Around("execution(* com.example.demo.controller.*.*(..))")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Around ⽅法开始执⾏");Object result = joinPoint.proceed();log.info("Around ⽅法结束执⾏");return result;}}

编写测试程序:

 package com.example.demo.controller; import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RequestMapping("/test") @RestControllerpublic class TestController { @RequestMapping("/t1")public String t1() {return "t1";} @RequestMapping("/t2")public boolean t2() {int a = 10 / 0;return true;}}

运行程序,观察日志:
1.正常运行的情况

http://127.0.0.1:8080/test/t1

观察日志

程序正常运行的情况下,@AfterThrowing标识的通知方法不会执行
从上图可以看出,@Around标识的通知方法包含两部分,一个"前置逻辑",一个"后置逻辑",其中"前置逻辑"会先于@Before标识的通知方法执行,"后置逻辑"会晚于@After标识的通知方法执行

2.异常时的情况

http://127.0.0.1:8080/test/t2

观察日志: 

程序发生异常的情况下:
@AfterReturning标识的通知方法不会执行@AfterThrowing标识的通知方法执行了
@Around环绕通知中原始方法调用时有异常,通知中的环绕后的代码逻辑也不会在执行

注意事项:
@Around环绕通知需要调用ProceedingJoinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标方法执行.
@Around环绕通知方法的返回值,必须指定为Object来接收原始方法的返回值,否则原始方法执行完毕,是获取不到返回值的.
一个切面类可以有多个切点.

 5.@PointCut


上面代码存在一个问题,就是存在大量重复的切点表达式execution(* com.example.demo.controller.*.*(..)),

Spring提供@PointCut注解,把公共的切点表达式提取出来,需要用到时引用该切入点表达式即可.
上述代码就可以修改为:

 @Slf4j@Aspect@Componentpublic class AspectDemo {//定义切点(公共的切点表达式)@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt(){}//前置通知@Before("pt()")public void doBefore() {//...代码省略} //后置通知@After("pt()")public void doAfter() {//...代码省略} //返回后通知@AfterReturning("pt()")public void doAfterReturning() {//...代码省略} //抛出异常后通知@AfterThrowing("pt()")public void doAfterThrowing() {//...代码省略} //添加环绕通知     @Around("pt()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {//...代码省略}}

注意:

当切点定义使用private修饰时,仅能在当前切面类中使用,当其他切面类也要使用当前切点定义时,就需要把private改为public.

引用方式为:全限定类名.方法名()

 @Slf4j@Aspect@Componentpublic class AspectDemo2 { //前置通知@Before("com.example.demo.aspect.AspectDemo.pt()")public void doBefore() {log.info("执⾏  AspectDemo2 -> Before ⽅法");}}

 6.切面优先级 @Order

当我们在一个项目中,定义了多个切面类并且这些切面类的多个切入点都匹配到了同一个目标方法

当目标方法运行的时候,这些切面类中的通知方法都会执行,那么这几个通知方法的执行顺序是什么样的呢?
我们还是通过程序来求证:

定义多个切面类:

为简单化,只写@Before和aAfter两个通知:

 @Componentpublic class AspectDemo1 {@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt(){}//前置通知@Before("pt()")public void doBefore() {log.info("执⾏  AspectDemo1 -> Before ⽅法");} //后置通知@After("pt()")public void doAfter() {log.info("执⾏  AspectDemo1 -> After ⽅法");}}
 @Componentpublic class AspectDemo2 {@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt(){}//前置通知@Before("pt()")public void doBefore() {log.info("执⾏  AspectDemo2 -> Before ⽅法");} //后置通知@After("pt()")public void doAfter() {log.info("执⾏  AspectDemo2 -> After ⽅法");}}
 @Componentpublic class AspectDemo3 {@Pointcut("execution(* com.example.demo.controller.*.*(..))")private void pt(){}//前置通知@Before("pt()")public void doBefore() {log.info("执⾏  AspectDemo3 -> Before ⽅法");} //后置通知@After("pt()")public void doAfter() {log.info("执⾏  AspectDemo3 -> After ⽅法");}}

运行程序,访问接口:

http://127.0.0.1:8080/test/t1

观察日志:
 

通过上述程序的运行结果,可以看出:
存在多个切面类时,默认按照切面类的类名字母排序:
@Before通知:字母排名靠前的先执行
@After通知:字母排名靠前的后执行
但这种方式不方便管理,我们的类名更多还是具备一定含义的.
Spring给我们提供了一个新的注解来控制这些切面通知的执行顺序:@Order

使用方式如下:
 

 @Aspect@Component@Order(2)public class AspectDemo1 {//...代码省略 }@Aspect@Component@Order(1)public class AspectDemo2 {//...代码省略 }@Aspect@Component@Order(3)public class AspectDemo3 {//...代码省略 }

重新运行程序,访问接口:

http://127.0.0.1:8080/test/t1

观察日志:

通过上述程序的运行结果,得出结论:
@Order注解标识的切面类,执行顺序如下:
@Before通知:数字越小先执行
@After通知:数字越大先执行
@Order控制切面的优先级,先执行优先级较高的切面,再执行优先级较低的切面,最终执行目标方法。

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

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

相关文章

SwiftUI 6.0(iOS 18/macOS 15)关于颜色 Color 的新玩法

概览 WWDC 2024 重装升级的 SwiftUI 6.0 让 Apple 不同平台&#xff08;iOS 18/macOS 15&#xff09;显得愈发的冰壶玉衡、美轮美奂。 之前梦寐以求的颜色混合功能在 WWDC 24 里终于美梦成真啦&#xff01; 在本篇博文中&#xff0c;您将学到如下内容&#xff1a; 概览1. 梦想…

PyCharm安装requests失败解决方法

PyCharm安装request失败解决方法&#xff0c;尝试各种方法均未能成功安装 失败一&#xff1a; 若用如下图方法 失败二&#xff1a;在桌面终端命令安装后&#xff0c;在Pycharm中导包依旧显示红色报错&#xff0c;未安装 采用如下方法&#xff0c;选择Pycharm终端Terminal&…

PHP-CGI的漏洞(CVE-2024-4577)

通过前两篇文章的铺垫&#xff0c;现在我们可以了解 CVE-2024-4577这个漏洞的原理 漏洞原理 CVE-2024-4577是CVE-2012-1823这个老漏洞的绕过&#xff0c;php cgi的老漏洞至今已经12年&#xff0c;具体可以参考我的另一个文档 简单来说&#xff0c;就是使用cgi模式运行的PHP&…

MOD和DIV的区别说明

1.说明 div 是取 A/BC&#xff0c;得到是C的值&#xff1b; mod 是取 A/BC...D,得到余数D的值。 2.实践 A8,B3,C2,D2 A9,B3,C3,D0

13.1.k8s集群的七层代理-ingress资源(进阶知识)

目录 一、ingress概述 1.前言 2.问题 3.ingress资源 二、ingress-nginx是什么 三、ingress-nginx 实现原理 四、部署ingress-nginx 1.获取部署文件 ingress-nginx.yaml 2.部署ingress-nginx 3.检查部署是否成功 五、编写使用Ingress样例代码 1.Ingress资源对象yaml文…

PhotoShop自动生成号码牌文件

1、说明 设计卡牌的时候&#xff0c;遇到自动生成编号&#xff0c;从01500到-02500&#xff0c;一个一个的手写&#xff0c;在存储保存成psd格式的文件&#xff0c;会很耗时。 下面将介绍如何使用ps自动生成psd格式的文件 2、使用excle生成数字 从01500到-02500 第一步&…

做现货白银模拟账户太爽,一做实盘就亏?

太多现货白银投资者有过这样的经历&#xff1a;自己做模拟账户的时候如鱼得水&#xff0c;盈利几乎手到拿来&#xff0c;实在爽得不要不要的&#xff0c;但在实盘操作中却常常面临亏损&#xff0c;甚至出现巨亏的情况。其实这主要是由于模拟交易与实盘交易之间存在显著的差异&a…

VBA技术资料MF166:提取某区域特定数据到工作表

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

Python武器库开发-武器库篇之Thinkphp5 SQL注入漏洞(六十六)

Python武器库开发-武器库篇之Thinkphp5 SQL注入漏洞&#xff08;六十六&#xff09; 漏洞环境搭建 这里我们使用Kali虚拟机安装docker并搭建vulhub靶场来进行ThinkPHP漏洞环境的安装&#xff0c;我们进入 ThinkPHP漏洞环境&#xff0c;可以 cd ThinkPHP&#xff0c;然后通过 …

3d怎么把歪的模型摆正?---模大狮模型网

在进行3D建模过程中&#xff0c;有时候会遇到模型出现歪曲或者旋转不正确的情况&#xff0c;这可能会影响到后续的设计和渲染效果。因此&#xff0c;学会将歪曲的模型摆正是一个非常重要的技巧。模大狮将介绍几种常用的方法&#xff0c;帮助您有效地将歪曲的3D模型摆正&#xf…

Python19 lambda表达式

在 Python 中&#xff0c;lambda 表达式是一个小型匿名函数&#xff0c;通常用于实现简单、单行的函数。lambda 函数可以接受任意数量的参数&#xff0c;但只能有一个表达式。 基本语法&#xff1a; lambda arguments: expression这里&#xff0c;arguments 是传递给 lambda …

精益思想在机器人开发中的应用体现

精益思想源于制造业&#xff0c;旨在通过消除浪费、优化流程、持续改进来提升企业竞争力。在机器人开发中&#xff0c;精益思想同样具有指导意义。它要求开发团队在需求分析、设计、制造、测试等各个环节中&#xff0c;不断追求精益求精&#xff0c;力求在降低成本的同时提升产…

多路h265监控录放开发-(14)

首先创建一个新类XCalendar继承QCalendarWidget类&#xff0c;然后在UI视图设计器中把日历提升为XCalendar&#xff0c;通过这个函数自己设置日历的样式 xcalendar.h #pragma once #include <QCalendarWidget> class XCalendar :public QCalendarWidget { public:XCal…

论文神器:即插即用归一化模型!无缝插入助力涨点!

归一化是深度学习和机器学习中一个非常重要的步骤&#xff0c;它通过对数据或网络层的输出进行变换&#xff0c;使其符合特定的标准&#xff0c;有效缓解不同特征间由于量纲和数值范围差异造成的影响&#xff0c;加速模型的收敛速度&#xff0c;并提高模型精度。 大多数归一化…

【ONLYOFFICE深度探索】:ONLYOFFICE桌面编辑器8.1震撼发布,打造高效办公新境界

文章目录 一、功能完善的PDF编辑器&#xff1a;解锁文档处理新维度二、幻灯片版式设计&#xff1a;释放创意&#xff0c;打造专业演示三、改进从右至左显示&#xff1a;尊重多元文化&#xff0c;优化阅读体验四、新增本地化选项&#xff1a;连接全球用户&#xff0c;跨越语言障…

os7安装gitlab

gitlab安装要求&#xff1a;os7以上版本&#xff0c;4G内存&#xff0c;磁盘50GB 1.克隆 由于我这里不想影响原来的&#xff0c;所以这里克隆一个os系统。如果其他是第一次安装则不用。 2.修改ip地址 cd /etc/sysconfig/network-scriptsvi ifcfg-ens33 按&#xff1a;insert…

对于GPT-5的些许期待

目录 1.概述 2.GPT-5技术突破预测 3.智能系统人类协作 3.1. 辅助决策 3.2. 增强创造力 3.3. 处理复杂任务 3.4.人机协同的未来图景 4.迎接AI技术变革策略 4.1.教育方面 4.2.职业发展方面 4.3.政策制定方面 4.4.人才与技能培养 1.概述 GPT-5作为下一代大语言模型&a…

cityscapes数据集转换为COCO数据集格式【速来,我吃过的苦,兄弟们就别再吃了】

利用CityScapes数据集&#xff0c;将其转换为COCO格式的实例分割数据集 – – – 进而再训练出新的YOLOv8-seg模型 写个前言&#xff1a; 人嘛&#xff0c;总想着偷点懒&#xff0c;有现成的数据集&#xff0c;就得拿来用&#xff0c;是吧&#xff1f;确实是这样。 接下来的步…

开启调试模式

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 run()方法虽然适用于启动本地的开发服务器&#xff0c;但是每次修改代码后都要手动重启它。这样并不够方便&#xff0c;如果启用了调试支持&#xff…

AIGC-商业设计大师班,商业设计全流程(22节课)

课程内容&#xff1a; 02.AIGC大师计划(百天磨炼&#xff0c;只为让你一次成为大师).mp4 03.这5个细心的翻译工具我想全部告诉你(感受不到的工具才是好工具),mp4 04.扎实的基础是成功的关键(汇聚精华指导新功能演示方法).mp4 05.AI绘画大师级十二体咒语书写(大师级起步).mp…