详解Spring AOP(一)

news2024/11/23 4:14:21

目录

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
 @Component
 public 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") 
 @RestController
 public 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
 @Component
 public 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") 
 @RestController
 public 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
 @Component
 public 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
 @Component
 public class AspectDemo2 { 
     //前置通知
     @Before("com.example.demo.aspect.AspectDemo.pt()")
     public void doBefore() {
        log.info("执⾏  AspectDemo2 -> Before ⽅法");
     }
 }

 6.切面优先级 @Order

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

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

定义多个切面类:

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

 @Component
 public 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 ⽅法");
     }
 }
 @Component
 public 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 ⽅法");
     }
 }
 @Component
 public 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.coloradmin.cn/o/1850551.html

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

相关文章

高考学计算机专业的小白指南,一些比较实用的软件,快速入手计算机

计算机指南 哈喽&#xff0c;首先先和大家分享一下之前我用电脑觉得还行的使用方法吧 这里的方法其实是受MIT计算机课教程的启发然后和自己使用计算机过程中得出来的经验结论 所以有错的地方欢迎大家指出&#xff0c;感谢 那么&#xff0c;我们就开始吧~~~ 杀毒软件 一款…

基本循环神经网络(RNN)

RNN背景&#xff1a;RNN与FNN 在前馈神经网络中&#xff0c;信息的传递是单向的&#xff0c;这种限制虽然使得网络变得更容易学习&#xff0c;但在一定程度上也减弱了神经网络模型的能力。 在生物神经网络中&#xff0c;神经元之间的连接关系要复杂的多。前馈神经网络可以看着…

智慧校园综合管理系统的优点有哪些

在当今这个信息化飞速发展的时代&#xff0c;智慧校园综合管理系统正逐步成为教育领域的一股革新力量&#xff0c;它悄然改变着我们对传统校园管理的认知。这套系统如同一个无形的桥梁&#xff0c;将先进的信息技术与学校的日常运作紧密相连&#xff0c;展现出多维度的优势。 …

你好,复变函数2.0

第一行&#xff1a;0 或 1 第二行&#xff1a;&#xff08;空格&#xff09;函数&#xff08;后缀&#xff09; #pragma warning(disable:4996) #include <easyx.h> #include <stdio.h> #include <math.h> #define PI 3.141592653589793 #define E 2.71828…

基于MATLAB仿真LFM线性调频信号

基于MATLAB仿真LFM线性调频信号 目录 前言 一、LFM信号简介 二、LFM信号基本原理 三、LFM信号仿真 四、代码 总结 前言 仿真中的接收信号&#xff0c;有时为了简单会直接用一个正弦波代替&#xff0c;但实际中接收到的信号极少是点频信号&#xff0c;一般都是PSK信号、OF…

Zigbee协议详解:低功耗无线通信的理想选择

什么是Zigbee协议 Zigbee是一种基于IEEE 802.15.4标准的无线通信协议&#xff0c;专为低功耗、低数据速率和短距离通信设计。它广泛应用于物联网&#xff08;IoT&#xff09;设备&#xff0c;如智能家居、工业自动化和健康监测等领域。Zigbee协议由Zigbee联盟维护和推广&#x…

国企:2024年6月中国移动相关招聘信息

中国移动研究院: AI中心-大模型数据工程师 工作地点:北京市、西安市2 发布时间 :2024-06-18 学历要求:硕士研究生及以上 招聘人数:招聘若干人 专业要求 计算机、人工智能、软件工程、数学等相关专业 工作职责 1、负责处理和清洗大规模、多来源的数据集,保证数…

yum的概念、相关命令、ftp http部署步骤;NFS共享文件操作步骤

目录 yum 配置文件 缓存功能操作步骤 创建并配置本地仓库文件 yum相关命令 yum install __ yum repolist yum list __ yum info __ yum search __ yum whatprovides __ yum remove __ yum -y update __ yum history yum grouplist yum groupinstall "__&q…

【Netty】nio阻塞非阻塞Selector

阻塞VS非阻塞 阻塞 阻塞模式下&#xff0c;相关方法都会导致线程暂停。 ServerSocketChannel.accept() 会在没有建立连接的时候让线程暂停 SocketChannel.read()会在没有数据的时候让线程暂停。 阻塞的表现就是线程暂停了&#xff0c;暂停期间不会占用CPU&#xff0c;但线程…

DAY03 HTML

文章目录 一 表格1. 表格的语法2. 表格的可选标记3. 不规则的单元格&#xff08;合并单元格&#xff09;4. 表格的属性5. 表格的大小 二 列表1. 有序列表2. 无序列表3. 属性4. 列表的嵌套5. 定义列表【了解】 三 表单(重点)1. 表单的语法2. 表单的控件分类3. input元素4. selec…

jQuery 基本操作

01-简介 jQuery 是一个功能丰富且广泛使用的 JavaScript 库&#xff0c;它简化了 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作。jQuery 通过其易用的 API&#xff0c;使复杂的 JavaScript 编程任务变得更加简单&#xff0c;并且兼容各种浏览器。 1、jQuery特点 简化 DOM …

数据库设计概述-数据库设计内容、数据库设计方法(基于E-R模型的规范设计方法)

一、引言 如何利用关系数据库理论设计一个满足应用系统需求的数据库 二、数据库设计内容 1、数据库设计是基于应用系统需求分析中对数据的需求&#xff0c;解决数据的抽象、数据的表达和数据的存储结构等问题 2、其目标是设计出一个满足应用要求、简洁、高效、规范合理的数…

Python基础教程(三十):math模块

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; &#x1f49d;&#x1f49…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] LYA的巡演(100分) - 三语言AC题解(Python/Java/Cpp)

&#x1f36d; 大家好这里是清隆学长 &#xff0c;一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 &#x1f4bb; ACM银牌&#x1f948;| 多次AK大厂笔试 &#xff5c; 编程一对一辅导 &#x1f44f; 感谢大家的订阅➕ 和 喜欢&#x1f497; &#x1f…

Stm32超声波测距实验

一.任务需求 1. 采用stm32F103和HC-SR04超声波模块&#xff0c; 使用标准库或HAL库 定时器中断&#xff0c;完成1或2路的超声波障碍物测距功能。 2. 当前智能汽车上一般配置有12路超声波雷达&#xff0c;这些专用超声波雷达内置了MCU&#xff0c;直接输出数字化的测距结果&am…

分类判决界面---W-H、H-K算法

本篇文章是博主在人工智能等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对人工智能等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在AI学习笔记&#…

文件管理—linux(基础IO)

目录 ​编辑 一、C语言文件接口&#xff08;库函数&#xff09; hello.c写文件 hello.c读文件 输出信息到显示器 stdin & stdout & stderr 二、系统文件I/O&#xff08;系统调用&#xff09; hello.c 写文件&#xff1a; hello.c读文件 接口介绍 open open…

基于Java学生选课管理系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

STM32多种开发环境及生成hex及bin文件介绍

一.STM32开发环境 KEIL系列 &#xff08;1&#xff09;KEIL公司目前有四款独立的嵌入式软件开发工具&#xff0c;即MDK、KEIL C51、KEIL C166、KEIL C251&#xff0c;它们都是KEIL公司品牌下的产品&#xff0c;都基于uVision集成开发环境&#xff0c;其中MDK是RealView系列中…

从零对Transformer的理解(台大李宏毅)

Self-attention layer自注意力 对比与传统cnn和rnn&#xff0c;都是需要t-1时刻的状态然后得到t时刻的状态。我不知道这样理解对不对&#xff0c;反正从代码上看我是这么认为的。而transformer的子注意力机制是在同一时刻产生。意思就是输入一个时间序列&#xff0c;在计算完权…