【JavaEE进阶】Spring AOP入门

欢迎关注个人主页:逸狼


创造不易,可以点点赞吗

如有错误,欢迎指出~


AOP是Spring框架的第⼆⼤核⼼(第⼀⼤核⼼是 IoC)

什么是AOP?

• AspectOrientedProgramming(⾯向切⾯编程) 什么是⾯向切⾯编程呢?

切⾯就是指某⼀类特定问题,所以AOP也可以理解为⾯向特定⽅法编程.

什么是⾯向特定⽅法编程呢?⽐如"登录校验",就是⼀类特定问题.登录校验拦截器,就是对"登录校验"这类问题的统⼀处理.所以,拦截器也是AOP的⼀种应⽤.AOP是⼀种思想,拦截器是AOP 思想的⼀种实现.Spring框架实现了这种思想,提供了拦截器技术的相关接⼝.

同样的,统⼀数据返回格式和统⼀异常处理,也是AOP思想的⼀种实现. 简单来说: AOP是⼀种思想,是对某⼀类事情的集中处理.

什么是SpringAOP?

AOP是⼀种思想,它的实现⽅法有很多,有SpringAOP,也有AspectJ、CGLIB等. SpringAOP是其中的⼀种实现⽅式. 学会了统⼀功能之后,是不是就学会了SpringAOP呢,当然不是. 拦截器作⽤的维度是URL(⼀次请求和响应),@ControllerAdvice 应⽤场景主要是全局异常处理 (配合⾃定义异常效果更佳),数据绑定,数据预处理.AOP作⽤的维度更加细致(可以根据包、类、⽅法 名、参数等进⾏拦截),能够实现更加复杂的业务逻辑.

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

比如想要记录每个方法的耗时 ,记录开始时间,结束时间,再计算耗时,如果是常规写法,每个方法都要重复书写这些代码,AOP就是将这些重复代码提取出来,

AOP可以在不改变原有的代码的前提下, 增强原来方法的功能(⽆侵⼊性:解耦) 

    //通过id查询图书@RequestMapping("/queryBookById")public BookInfo queryBookById(Integer bookId){long start = System.currentTimeMillis();log.info("获取图书信息, bookId: "+ bookId);//参数校验,不能为null,不能<=0...省略BookInfo bookInfo = bookService.queryBookById(bookId);long end = System.currentTimeMillis();log.info("queryBookById 耗时: " + (end - start) + "ms");return bookInfo;}

 SpringAOP快速⼊⻔

学习什么是AOP后,我们先通过下⾯的程序体验下AOP的开发,并掌握Spring中AOP的开发步骤.

需求:统计图书系统各个接⼝⽅法的执⾏时间.

引⼊AOP依赖

在pom.xml⽂件中添加配置

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

统计执⾏时间

package com.example.demo.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;@Aspect
@Component
@Slf4j
public class TimeRecordAspect {//作用域,执行路径@Around("execution(* com.example.demo.controller.*.*(..))")public Object timeRecord(ProceedingJoinPoint pjt){//1.记录开始时间//2.执行目标方法时间//3.记录结束时间//4.返回结果long start = System.currentTimeMillis();//执行目标方法Object o = null;try {o = pjt.proceed();} catch (Throwable e) {e.printStackTrace();}long end = System.currentTimeMillis();log.info(pjt.getSignature() + "耗时: "+ (end - start)+ "ms");return o;}
}
  • 1. @Aspect:标识这是⼀个切⾯类
  • 2. @Around:环绕通知,在⽬标⽅法的前后都会被执⾏.后⾯的表达式表⽰对哪些⽅法进⾏增强.
  • 3. ProceedingJoinPoint.proceed()让原始⽅法执⾏

我们通过AOP⼊⻔程序完成了业务接⼝执⾏耗时的统计. 通过上⾯的程序,我们也可以感受到AOP⾯向切⾯编程的⼀些优势:

  1. 代码⽆侵⼊:不修改原始的业务⽅法,就可以对原始的业务⽅法进⾏了功能的增强或者是功能的改变
  2. 减少了重复代码
  3. 提⾼开发效率
  4. 维护⽅便

SpringAOP核⼼概念

切点(Pointcut)

切点(Pointcut),也称之为"切⼊点" Pointcut的作⽤就是提供⼀组规则(使⽤AspectJpointcutexpressionlanguage来描述),告诉程序对 哪些⽅法来进⾏功能增强.

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

连接点(JoinPoint)

满⾜切点表达式规则的⽅法,就是连接点.也就是可以被AOP控制的具体⽅法 以⼊⻔程序举例,所有com.example.demo.controller 路径下的⽅法,都是连接点.

切点和连接点的关系 : 

连接点是满⾜切点表达式的元素.

切点可以看做是保存了众多连接点的⼀个集合.

通知(Advice)

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

切⾯(Aspect)

切⾯(Aspect)=切点(Pointcut)+通知(Advice) 通过切⾯就能够描述当前AOP程序需要针对于哪些⽅法,在什么时候执⾏什么样的操作.切⾯既包含了通知逻辑的定义,也包括了连接点的定义.

切⾯所在的类,我们⼀般称为切⾯类(被@Aspect注解标识的类

通知类型

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

  • @Around:环绕通知,此注解标注的通知⽅法在⽬标⽅法前,后都被执⾏
  • @Before:前置通知,此注解标注的通知⽅法在⽬标⽅法前被执⾏
  • @After:后置通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,⽆论是否有异常都会执⾏
  • @AfterReturning:返回后通知,此注解标注的通知⽅法在⽬标⽅法后被执⾏,有异常不会执⾏
  • @AfterThrowing:异常后通知,此注解标注的通知⽅法发⽣异常后执⾏

示例代码

package com.example.demo.aspect;import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;@Slf4j
@Component
@Aspect
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;}
}

 程序正常运⾏的情况下,@AfterThrowing 标识的通知⽅法不会执⾏

从上图也可以看出来,@Around 标识的通知⽅法包含两部分,⼀个"前置逻辑",⼀个"后置逻辑".其 中"前置逻辑"会先于 @Before 标识的通知⽅法执⾏,"后置逻辑"会晚于 @After 标识的通知⽅法执⾏ 

如果发生异常


 

程序发⽣异常的情况下:

@AfterReturning 标识的通知⽅法不会执⾏, @AfterThrowing 标识的通知⽅法执⾏了

@Around 环绕通知中原始⽅法调⽤时有异常,通知中的环绕后的代码逻辑也不会在执⾏了(因为 原始⽅法调⽤出异常了)

@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() {//...代码省略 }

当切点定义使⽤private修饰时,仅能在当前切⾯类中使⽤,当其他切⾯类也要使⽤当前切点定义时,就需 要把private改为public.引⽤⽅式为:全限定类名.⽅法名() 

public class TimeRecordAspect {//    @Around("execution(* com.example.demo.controller.*.*(..))")@Around("com.example.demo.aspect.AspectDemo.pt()")public Object timeRecord(ProceedingJoinPoint pjt){
...}

切⾯优先级@Order

当我们在⼀个项⽬中,定义了多个切⾯类时,并且这些切⾯类的多个切⼊点都匹配到了同⼀个⽬标⽅法. 当⽬标⽅法运⾏的时候,这些切⾯类中的通知⽅法都会执⾏,那么这⼏个通知⽅法的执⾏顺序是什么样 的呢?

存在多个切⾯类时,默认按照切⾯类的类名字⺟排序: • @Before 通知:字⺟排名靠前的先执⾏ • @After 通知:字⺟排名靠前的后执⾏

但这种⽅式不⽅便管理,我们的类名更多还是具备⼀定含义的. Spring给我们提供了⼀个新的注解,来控制这些切⾯通知的执⾏顺序:@Order 使⽤⽅式如下:

@Slf4j
@Component
@Aspect
@Order(3)
public class demo1 {
...
}...
@Order(2)
public class demo2 {
...}...
@Order(1)
public class demo3 {
...}

@Order 控制切⾯的优先级,先执⾏优先级较⾼的切⾯,再执⾏优先级较低的切⾯,最终执⾏⽬标⽅法.数字越小,优先级越高

切点表达式

上⾯的代码中,我们⼀直在使⽤切点表达式来描述切点.下⾯我们来介绍⼀下切点表达式的语法. 切点表达式常⻅有两种表达⽅式

execution

@annotation

execution表达式

execution()是最常⽤的切点表达式,⽤来匹配⽅法,语法为:

execution(访问修饰符> 返回类型> 包名.类名.⽅法(⽅法参数)> 异常>)

其中:访问 修饰符 和 异常 可以省略

 

切点表达式⽰例

TestController下的 public修饰,返回类型为String⽅法名为t1,⽆参⽅法

execution(public String com.example.demo.controller.TestController.t1())

省略访问修饰符

execution(String com.example.demo.controller.TestController.t1())

匹配所有返回类型

execution(* com.example.demo.controller.TestController.t1())

匹配TestController下的所有⽆参⽅法

execution(* com.example.demo.controller.TestController.*())

匹配TestController下的所有⽅法

execution(* com.example.demo.controller.TestController.*(..))

匹配controller包下所有的类的所有⽅法

execution(* com.example.demo.controller.*.*(..))

匹配所有包下⾯的TestController

execution(* com..TestController.*(..))

匹配com.example.demo包下,⼦孙包下的所有类的所有⽅法

execution(* com.example.demo..*(..))

@annotation

execution表达式更适⽤有规则的,如果我们要匹配多个⽆规则的⽅法呢,⽐如:TestController中的t1() 和UserController中的u1()这两个⽅法. 这个时候我们使⽤execution这种切点表达式来描述就不是很⽅便了. 我们可以借助⾃定义注解的⽅式以及另⼀种切点表达式 @annotation 来描述这⼀类的切点

实现步骤:

1. 编写⾃定义注解

2. 使⽤ @annotation 表达式来描述切点

3. 在连接点的⽅法上添加⾃定义注解

⾃定义注解

@TimeRecord 创建⼀个注解类(和创建Class⽂件⼀样的流程,选择Annotation就可以了)

package com.example.demo.aspect;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)//运行时
@Target({ElementType.METHOD})//表示作用在方法上public @interface TimeRecord {
}

@Target 标识了 Annotation 所修饰的对象范围,即该注解可以⽤在什么地⽅.

@Retention 指Annotation被保留的时间⻓短,标明注解的⽣命周期

切⾯类

使⽤ @annotation 切点表达式定义切点,只对@TimeRecord⽣效

@Aspect
@Component
@Slf4j
public class TimeRecordAspect {@Around("@annotation(com.example.demo.aspect.TimeRecord)")public Object timeRecord(ProceedingJoinPoint pjt){//1.记录开始时间//2.执行目标方法时间//3.记录结束时间//4.返回结果long start = System.currentTimeMillis();log.info("timeRecord.Around ⽅法开始执⾏");//执行目标方法Object o = null;try {o = pjt.proceed();} catch (Throwable e) {e.printStackTrace();}long end = System.currentTimeMillis();log.info(pjt.getSignature() + "耗时: "+ (end - start)+ "ms");log.info("timeRecord.Around ⽅法结束执⾏");return o;}
}

在TestController中的t1()和UserController中的u1()这两个⽅法上添加⾃定义注解@TimeRecord ,其他⽅法不添加

@RequestMapping("/test")
@RestController
@Slf4j
public class TestController {@TimeRecord@RequestMapping("/t1")public String t1(){log.info("执行t1");return "t1";}@RequestMapping("/t2")public int t2(){log.info("执行t2");  return "t2";}
@RequestMapping("/user")
@RestController
@Slf4j
public class UserController {@TimeRecord@RequestMapping("/u1")public String u1(){log.info("执行u1");return "u1";}@RequestMapping("/u2")public String u2(){log.info("执行u2");return "u2";}
}

如果要让所有带有@RequestMapping注解的方法都实现记录时间,只需要将上面的切点表达式换成以下

    @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)")

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

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

相关文章

算法思想之双指针(一)

欢迎拜访&#xff1a;雾里看山-CSDN博客 本篇主题&#xff1a;算法思想之双指针(一) 发布时间&#xff1a;2025.4.4 隶属专栏&#xff1a;算法 目录 双指针算法介绍对撞指针&#xff1a;快慢指针&#xff1a; 例题移动零题目链接题目描述算法思路代码实现 复写零题目链接题目描…

【11408学习记录】英语写作黄金模板+语法全解:用FTC数据泄漏案掌握书信结构与长难句拆解(附思维导图)

2025.04.04 英语写作书信写作第一段私人信件公务信函 语法总结——简单句简单句的核心&#xff1a;谓语动词的变化词性的拓展限定词 形容词与副词介词短语 成分的扩展同位语插入语 非谓语动词 每日一句词汇 第一步&#xff1a;辨别第二步&#xff1a;断开第三步&#xff1a;简化…

手机显示5GA图标的条件

最近有星友问在什么情况下才能显示5G-A&#xff1f;虽然这个我也不知道&#xff0c;但是我有几个运营商的5G终端白皮书&#xff0c;从上面就可以找到答案。 如上是几个运营商显示5G-A的条件&#xff0c;基本上考虑的都是3CC的情况&#xff0c;联通还有考虑200M CA 2CC的场景&am…

网络:华为数通HCIA学习:IP路由基础

华为HCIA学习 IP路由基础路由协议或路由种类以及对应路由的优先级按工作区域分类&#xff1a;按工作机制及算法分类&#xff1a;路由的优先级路由器选择最优路由的顺序是什么? 前言自治系统LAN和广播域路由选路IP路由表路由度量建立路由表最长匹配原则路由器转发数据包总结 IP…

Docker 镜像相关的基本操作

一、Docker 镜像基本操作 1. 查找镜像 命令&#xff1a; docker search <镜像名称> 示例&#xff1a;查找 CentOS 镜像&#xff1a; docker search centos 命令解释&#xff1a; 默认从 Docker Hub 官方仓库上搜索镜像。搜索结果包含多个列&#xff1a; NAME&…

Linux文件特殊权限管理及进程和线程

acl 权限优先级 拥有者 > 特殊指定用户 > 权限多的组 >权限少的组 > 其他 mask阈值 mask是能够赋予指定用户权限的最大阀值 当设定完毕文件的acl列表之后用chmod缩小了文件拥有组的权力 mask会发生变化 恢复&#xff1a; setfacl -m m: 权限 :rwx 文件/…

NVIDIA AgentIQ 详细介绍

NVIDIA AgentIQ 详细介绍 1. 引言 NVIDIA AgentIQ 是一个灵活的库&#xff0c;旨在将企业代理&#xff08;无论使用何种框架&#xff09;与各种数据源和工具无缝集成。通过将代理、工具和代理工作流视为简单的函数调用&#xff0c;AgentIQ 实现了真正的可组合性&#xff1a;一…

算法设计与分析5(动态规划)

动态规划的基本思想 将一个问题划分为多个不独立的子问题&#xff0c;这些子问题在求解过程中可能会有些数据进行了重复计算。我们可以把计算过的数据保存起来&#xff0c;当下次遇到同样的数据计算时&#xff0c;就可以查表直接得到答案&#xff0c;而不是再次计算 动态规划…

怎么理解量子比特模型,迁移到量子计算机开始编程

怎么理解量子比特模型&#xff0c;迁移到量子计算机开始编程 视频链接&#xff1a; 好的现在是2025年的3月最后一天,3月31号,今天我们讨论的话题是量子编程,也就是在量子计算机上,使用特定的语言进行软件开发。当然我们要讨论的,不是,量子编程的某一门语言的技术细节,而是考虑…

使用Expo框架开发APP——详细教程

在移动应用开发日益普及的今天&#xff0c;跨平台开发工具越来越受到开发者青睐。Expo 是基于 React Native 的一整套工具和服务&#xff0c;它能够大幅降低原生开发的门槛&#xff0c;让开发者只需关注业务逻辑和界面实现&#xff0c;而不用纠结于复杂的原生配置。本文将从零开…

windows技术基础知识

NT架构 NT 就是new techonology 的英文单词缩写&#xff0c;是微软1993年推出操作系统的重大升级&#xff0c;如内存管理&#xff0c;安全机制&#xff0c;多任务&#xff0c;多线程支持。在此之前操作系统都是基于MS-DOS上面的图形化界面&#xff0c;只有有限的内存管理和多任…

迪杰斯特拉+二分+优先队列+拓扑+堆优化(奶牛航线Cowroute、架设电话线dd、路障Roadblocks、奶牛交通Traffic)

原文地址 https://fmcraft.top/index.php/Programming/2025040402.html 主要算法 迪杰斯特拉Dijkstra 题目列表 P1&#xff1a;奶牛航线Cowroute 题目描述 题目描述 Bessie已经厌倦了农场冬天的寒冷气候&#xff0c;她决定坐飞机去更温暖的地方去度假。不幸的是&#xf…

#Liunx内存管理# 在32bit Linux内核中,用户空间和内核空间的比例通常是3:1,可以修改成2:2吗?

在32位Linux内核中&#xff0c;用户空间和内核空间的3:1默认比例可以修改为2:2&#xff0c;但需要权衡实际需求和潜在影响。以下是具体分析&#xff1a; 一、修改可行性 1.技术实现 通过内核启动参数调整虚拟地址空间划分&#xff0c;例如在GRUB配置中添加mem2G参数&#xff0c…

JAVA:使用 Curator 进行 ZooKeeper 操作的技术指南

1、简述 Apache Curator 是一个基于 ZooKeeper 的 Java 客户端库&#xff0c;它极大地简化了使用 ZooKeeper 的开发工作。Curator 提供了高层次的 API&#xff0c;封装了很多复杂的 ZooKeeper 操作&#xff0c;例如连接管理、分布式锁、Leader 选举等。 在分布式系统中&#…

Julia语言的测试覆盖率

Julia语言的测试覆盖率探讨 引言 在现代软件开发中&#xff0c;测试是确保软件质量的重要环节。随着软件的复杂度不断增加&#xff0c;测试覆盖率作为衡量测试质量的一个重要指标&#xff0c;受到了越来越多开发者的关注。Julia语言作为一种高性能的动态编程语言&#xff0c;…

【万字总结】前端全方位性能优化指南(八)——Webpack 6调优、模块联邦升级、Tree Shaking突破

构建工具深度优化——从机械配置到智能工程革命 当Webpack配置项突破2000行、Node进程内存耗尽告警时,传统构建优化已触及工具链的物理极限:Babel转译耗时占比超60%、跨项目模块复用催生冗余构建、Tree Shaking误删关键代码引发线上事故……构建流程正从「工程问题」演变为「…

使用MCP服务器实现AI任务完成通知:让Cursor更智能

0. 简介 在使用AI工具进行长时间任务时&#xff0c;常常需要等待结果。MCP&#xff08;Model Context Protocol&#xff09;服务器"mcp_server_notify"提供了一个优雅的解决方案&#xff0c;让AI在完成任务后通过系统通知提醒你。本文将介绍如何在Cursor中配置和使用…

Java面试黄金宝典33

1. 什么是存取控制、 触发器、 存储过程 、 游标 存取控制 定义&#xff1a;存取控制是数据库管理系统&#xff08;DBMS&#xff09;为保障数据安全性与完整性&#xff0c;对不同用户访问数据库对象&#xff08;如表、视图等&#xff09;的权限加以管理的机制。它借助定义用户…

DataX实战教程

需求&#xff1a; 用datax同步mysql&#xff1a; 192.168.236.134中test1库的user表到192.168.236.136中test1库的user表 步骤&#xff1a; 下载安装包 https://github.com/alibaba/DataX/blob/master/userGuid.md 进入引导页 https://github.com/alibaba/DataX/blob/ma…

C#/.NET/.NET Core技术前沿周刊 | 第 32 期(2025年3.24-3.31)

前言 C#/.NET/.NET Core技术前沿周刊&#xff0c;你的每周技术指南针&#xff01;记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿&#xff0c;助力技术成长与视野拓宽。 欢迎投稿、推荐…