【JavaWeb后端学习笔记】Spring AOP面向切面编程
AOP
- 1、Spring AOP概述
- 2、SpringAOP快速入门
- 3、SpringAOP核心概念
- 4、通知类型
- 5、通知顺序
- 6、切入点表达式
- 6.1 execution方式
- 6.2 @annotation方式
- 7、连接点
1、Spring AOP概述
AOP:Aspect Oriented Programming,面向特定方法编程。
AOP是通过动态代理技术实现的。SpringAOP是Spring框架的高级技术,旨在管理Bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。
假设场景:现在需要优化一个刚开发好的系统,首先要记录每一个业务方法执行的时长,也就是在业务开始时记录开始时间,业务执行完毕时记录结束时间,时间差就是该业务的执行时间。只要在每一个业务方法中增加这个操作,就能记录所有业务方法的执行时间。但是由于业务可能非常多,这样做相当繁琐,工作量也大。这是可以通过AOP面向切面编程来优化该操作。
AOP会通过动态代理技术对原有方法进行改造。AOP首先会定义一个模板方法,在模板方法内定义在业务方法执行前记录开始时间,然后执行业务方法,业务方法执行后记录结束时间,获得时间差。这样就不需要对每个业务方法进行改造,而是通过AOP来改造了。
2、SpringAOP快速入门
以记录业务方法执行时间为例。
SpringAOP的使用分一下几步:
- 引入SpringAOP依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
- 编写AOP程序。首先定义一个类,在类上加@Component注解与@Aspect注解。@Component注解表明将这个类交给IOC容器管理,@Aspect声明当前类为AOP类。然后定义一个方法模板,在方法模板中记录开始时间,调用原始方法,再记录结束时间。注意给方法模板传入ProceedingJoinPoint类对象,通过该对象的proceed()方法能够调用原始方法。
- 给方法模板加切入点。指定哪些包里的那些接口和类中的方法需要通过AOP改造。
最后的简单实现如下:
@Slf4j
@Component // 交给IOC容器管理
@Aspect // 声明当前类为AOP类
public class TimeAspect {@Around("execution(* com.wrj.controller.*.*(..))") // 切入点表达式。指定com.wrj.controller下的所有接口和类中的所有方法都被改造public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {// 1. 记录开始时间long begin = System.currentTimeMillis();// 2. 调用原始方法运行Object result = joinPoint.proceed();// 3. 记录结束时间,计算方法耗时long end = System.currentTimeMillis();long times = end - begin;log.info(joinPoint.getSignature() + "方法执行耗时:{}", times + "ms");return result;}
}
3、SpringAOP核心概念
SpringAOP中有五大核心概念:
- 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息);
- 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法);
- 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用;
- 切面:Aspect,描述通知与切入点的对应关系(通知+切入点);
- 目标对象:Target,通知所应用的对象。
通过上面SpringAOP快速入门案例进行简单解释。
通知:在上面的案例中,需要记录方法的执行时间,因此需要先记录开始时间,再记录结束时间,最后计算执行时长,这三步共性代码就是通知。
切入点:案例中方法模板上加了一个注解,注解中写了一个切入点表达式。这个表达式是指定在com.wrj.controller包下的所有接口和类中的方法都被AOP改造。匹配的条件就是切入点。
@Around("execution(* com.wrj.controller.*.*(..))")
切面:切面是切入点和通知的组合。描述的是切入点与通知的关系。即切入点匹配到的方法需要执行通知中的公共代码。
连接点:在上面的案例中com.wrj.controller下的所有方法都是连接点,因为都可以被AOP改造。
目标对象:目标对象指通知应用的对象,此处指的是com.wrj.controller包下的类对象或接口实现类对象。
4、通知类型
通知类型有5种:
@Around
:环绕通知,此注解标注的通知方法在目标方法前、后都被执行;@Before
:前置通知,此注解标注的通知方法在目标方法前被执行;@After
:后置通知,此注解标注的通知方法在目标方法后被执行,无论是否有异常都会执行;- @AfterReturning:返回后通知,此注解标注的通知方法在目标方法后被执行,有异常不会执行;
- @AfterThrowing:异常后通知,此注解标注的通知方法发生异常后执行。
在使用环绕通知@Around时需注意注意:
- @Around环绕通知需要自己调用 ProceedingJoinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标方法执行
- @Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值。
5、通知顺序
如果有多个切面的切入点都匹配到了同一个目标方法,目标方法运行时,多个通知方法都会被执行。这时需要考虑通知顺序。
- 不同的切面类中,默认按照切面类的类名字排序:
- 目标方法前的通知方法:字母排名靠前的先执行
- 目标方法后的通知方法:字母排名靠前的后执行
- 用@Order(数字)加在切面类上来控制顺序
- 目标方法前的通知方法:数字小的先执行
- 目标方法后的通知方法:数字小的后执行
6、切入点表达式
切入点表达式有两种形式:execution方式与@annotation方式
6.1 execution方式
execution方式主要根据方法的返回值、包名、类名、方法名、方法参数等信息来匹配。
语法为:
execution(访问修饰符 返回值 包名.类名.方法名(方法参数全类名) throws 异常)
其中有几处可省略:
· 访问权限修饰符:可省略(比如:public、protected)
· 包名.类名.:可省略(注意类名后面也有点,不建议省略)
· throws异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)
在切入点表达式中,可以使用通配符描述切入点:
* :单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分。
.. :多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数。
- 在通知类型注解中通过切入点表达式设置切入点。
@Component
@Aspect
@Slf4j
public class DemoAspect {@Before("execution(* com.wrj.controller.*.*(..))")public void testBefore() {log.info("Before...前置通知");}@Around("execution(* com.wrj.controller.*.*(..))")public Object testAround(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Around...环绕通知,原始方法执行前逻辑");Object result = joinPoint.proceed();log.info("Around...环绕通知,原始方法执行后逻辑");return result;}@After("execution(* com.wrj.controller.*.*(..))")public void testAfter() {log.info("Before...后置通知");}
}
- 提取公共切入点。同样通过切入点表达式设置切入点,但是提取公共切入点。可以通过定义一个空方法,在空方法上加入注解@PointCut指定要抽取的公共的切入点。
@Component
@Aspect
@Slf4j
public class DemoAspect {// 1. 定义一个空方法,加上@PointCut注解抽取公共切入点@Pointcut("execution(* com.wrj.controller.*.*(..))")public void pt() {}@Before("pt()") // 2. 在通知类型注解中加入抽取的切入点public void testBefore() {log.info("Before...前置通知");}@Around("pt()")public Object testAround(ProceedingJoinPoint joinPoint) throws Throwable {log.info("Around...环绕通知,原始方法执行前逻辑");Object result = joinPoint.proceed();log.info("Around...环绕通知,原始方法执行后逻辑");return result;}@After("pt()")public void testAfter() {log.info("Before...后置通知");}
}
- 引用其他切面类的切入点。前提是有一个切面类中已经抽取除了公共切入点。并且其他的切面类有足够的访问权限访问该设置了公共切入点的方法。直接在通知类型注解中通过 包名.类名.方法名() 的方式引用。
@Slf4j
@Component // 交给IOC容器管理
@Aspect // 声明当前类为AOP类
public class TimeAspect {@Around("com.wrj.aspect.DemoAspect.pt()") // 引用com.wrj.aspect包下,DemoAspect类中的pt()方法上设置的切入点public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {// 1. 记录开始时间long begin = System.currentTimeMillis();// 2. 调用原始方法运行Object result = joinPoint.proceed();// 3. 记录结束时间,计算方法耗时long end = System.currentTimeMillis();long times = end - begin;log.info(joinPoint.getSignature() + "方法执行耗时:{}", times + "ms");return result;}
}
- 通过 ||、! 、&& 逻辑运算符连接多个切入点表达式。
@Component
@Aspect
@Slf4j
public class DemoAspect {// 使用 || 运算连接两个切入点表达式@Pointcut("execution(String com.wrj.controller.DemoController.demoFilter()) ||" +"execution(String com.wrj.controller.DemoController.demo())")public void pt() {}@Before("pt()")public void testBefore() {log.info("Before...前置通知");}
}
6.2 @annotation方式
- 自定义注解。在使用@annotation方式前需要自定义一个注解。自定义注解为空注解即可,不需要定义属性。
@Retention(RetentionPolicy.RUNTIME) // @Retention描述注解什么时候生效。RetentionPolicy.RUNTIME表示运行时生效
@Target(ElementType.METHOD) // @Target描述作用在哪些地方。ElementType.METHOD表示作用在方法上
public @interface MyAnnotation {
}
- 通过@annotation方式编写切入点表达式。在@annotation中写入自定义注解的全类名。含义是匹配加了自定义注解的方法。
@Component
@Aspect
@Slf4j
public class DemoAspect {// 在@annotation中写入自定义注解的全类名@Pointcut("@annotation(com.wrj.annotation.MyAnnotation)")public void pt() {}@Before("pt()")public void testBefore() {log.info("Before...前置通知");}
}
- 在需要被AOP改动的方法上加上自定义注解。
@RestController
@RequestMapping("/demo")
@Slf4j
public class DemoController {@MyAnnotation // 加自定义注解@GetMappingpublic String demoFilter() {System.out.println("demoFilter接口执行...");return "执行结束";}@MyAnnotation // 加自定义注解@GetMappingpublic String demo() {System.out.println("demoFilter接口执行...");return "执行结束";}
}
execution方式和@annotation方式可以混用。
7、连接点
在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,如目标类名、方法名、方法参数等。
对于@Around 通知,获取连接点信息只能使用 ProceedingJoinPoint。
对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型。
如要使用原始方法,需要自己在模板方法形参中指定JoinPoint或ProceedingJoinPoint对象。
一些常用的方法:
// 1. 获取 目标对象类名
String className = joinPoint.getTarget().getClass().getName();
// 2. 获取 目标方法的方法名
String methodName = joinPoint.getSignature().getName();
// 3. 获取 目标方法运行时传入的参数
Object[] args = joinPoint.getArgs();
// 4. 放行 目标方法执行并且返回目标方法运行的返回值
Object result = joinPoint.proceed();
注意:若模板方法需要返回原始方法返回的值,则模板方法的返回值类型需定义为Object。
相关文章:
【JavaWeb后端学习笔记】Spring AOP面向切面编程
AOP 1、Spring AOP概述2、SpringAOP快速入门3、SpringAOP核心概念4、通知类型5、通知顺序6、切入点表达式6.1 execution方式6.2 annotation方式 7、连接点 1、Spring AOP概述 AOP:Aspect Oriented Programming,面向特定方法编程。 AOP是通过动态代理技术…...

6.584-Lab5B
6.584-Lab5B Reference CodeReference BlogHomeworkMyself Code Sharded Key/Value Service 梗概 这个图是我从上面参考blog中拿来的,觉得做的不错,借助这张图来讲解一下需要一个什么样的 Service。 ShardCtrler Client: 接收来自客户发出的命…...

OceanBase 的探索与实践
作者:来自 vivo 互联网数据库团队- Xu Shaohui 本文总结了目前我们遇到的痛点问题并通过 OceanBase 的技术方案解决了这些痛点问题,完整的描述了 OceanBase 的实施落地,通过迁移到 OceanBase 实践案例中遇到的问题与解决方案让大家能更好的了…...

安卓调试环境搭建
前言 前段时间电脑重装了系统,最近准备调试一个apk,没想到装环境的过程并不顺利,很让人火大,于是记录一下。 反编译工具下载 下载apktool.bat和apktool.jar 官网地址:https://ibotpeaches.github.io/Apktool/install…...
动画Lottie
Lottie简介 Lottie是一个Airbnb 开发的用于Android,iOS,Web和Windows的库,用于解析使用Bodymovin导出为json的Adobe After Effects动画,并在移动设备和网络上呈现 — GitHub Lottie主要特性 After Effects 兼容性: …...

C++感受14-Hello Object 封装版 - 上
1. 封装即约束——封装和派生、多态的本质区别 一门计算机语言,要如何帮助程序员写出优秀的代码?两个方法:一是给程序员更多能力,二是给程序员更多约束。之前我们学习的派生和多态,更多的是给我们技能,而封…...

网络安全中大数据和人工智能应用实践
传统的网络安全防护手段主要是通过单点的网络安全设备,随着网络攻击的方式和手段不断的变化,大数据和人工智能技术也在最近十年飞速地发展,网络安全防护也逐渐开始拥抱大数据和人工智能。传统的安全设备和防护手段容易形成数据孤岛࿰…...
RISC-V架构下OP-TEE 安全系统实践
安全之安全(security)博客目录导读 本篇博客,我们聚焦RISC-V 2024中国峰会上的RISC-V和OP-TEE结合的一个安全系统实践,来自芯来科技桂兵老师。 关于RISC-V TEE(可信执行环境)的相关方案,如感兴趣可参考R...

40分钟学 Go 语言高并发:【实战】分布式缓存系统
【实战课程】分布式缓存系统 一、整体架构设计 首先,让我们通过架构图了解分布式缓存系统的整体设计: 核心组件 组件名称功能描述技术选型负载均衡层请求分发、节点选择一致性哈希缓存节点数据存储、过期处理内存存储 持久化同步机制节点间数据同步…...
[创业之路-186]:《华为战略管理法-DSTE实战体系》-1-为什么UTStarcom死了,华为却活了,而且越活越好?
目录 前言 一、市场定位与战略选择 二、技术创新能力 三、企业文化与团队建设 四、应对危机的能力 五、客户为中心的理念 六、市场适应性与战略灵活性 七、技术创新与研发投入 八、企业文化与团队建设 九、应对危机的能力 前言 UT斯达康(UTStarcom&#…...
python如何多行注释
在Python中,多行注释通常有两种方式: 使用三个单引号()或三个双引号(""")来创建多行字符串,这可以被用来作为多行注释。这种方式在Python中实际上是创建了一个多行的字符串对象…...
前端工程化面试题目常见
前端工程化面试常见题目包括: • 谈谈你对WebPack的认识。 • Webpack打包的流程是什么? • 说说你工作中几个常用的loader。 • 说说HtmlWebpackPlugin插件的作用。 • Webpack支持的脚本模块规范有哪些? • Webpack和gulp/grunt相比有什么特…...

定点数的乘除运算
原码一位乘法 乘积的符号由两个数的符号位异或而成。(不参与运算)被乘数和乘数均取绝对值参与运算,看作无符号数。乘数的最低位为Yn: 若Yn1,则部分积加上被乘数|x|,然后逻辑右移一位;若Yn0&…...

页面置换算法模拟 最近最久未使用(LRU)算法
最近最久未使用(LRU)算法是一种基于页面访问历史的页面置换算法。它选择最久未使用的页面进行置换。当需要访问一个不在内存中的页面时,如果内存已满,则选择最久未使用的页面进行置换。LRU算法通过记录页面的访问时间戳来判断页面…...
Ubuntu与Centos系统有何区别?
Ubuntu和CentOS都是基于Linux内核的操作系统,但它们在设计理念、使用场景和技术实现上有显著的区别。以下是详细的对比: 1. 基础和发行版本 Ubuntu: 基于Debian,使用.deb包管理系统。包含两个主要版本: LTSÿ…...

RK3568平台开发系列讲解(pinctrl 子系统篇)pinctrl_debug
🚀返回专栏总目录 文章目录 1. Overview2. debug信息2.1 pinctrl-devices2.2. pinctrl-handles2.3. pinctrl-handles3. debug信息3.1. 查看(pinctrl_register_pins)注册了哪些pins3.2. 查看pin groups;3.3. 查看每种functions所占用的gpio groups信息:3.4. pinconf沉淀、…...
避大坑!Vue3中reactive丢失响应式的问题
在vue3中,我们定义响应式数据无非是ref和reactive。 但是有的小伙伴会踩雷!导致定义的响应式丢失的问题。 reactive丢失响应式的情况1(直接赋值) 场景: 1.你定义了一个数据:let datareactive({name:"",age:"" }) 2.然后你…...

springSecurity权限控制
权限控制:不同的用户可以使用不同的功能。 我们不能在前端判断用户权限来控制显示哪些按钮,因为这样,有人会获取该功能对应的接口,就不需要通过前端,直接发送请求实现功能了。所以需要在后端进行权限判断。࿰…...
Pytorch训练固定随机种子(单卡场景和分布式训练场景)
模型的训练是一个随机过程,固定随机种子可以帮助我们复现实验结果。 接下来介绍一个模型训练过程中固定随机种子的代码,并对每条语句的作用都会进行解释。 def seed_reproducer(seed2333):random.seed(seed)os.environ["PYTHONHASHSEED"] s…...

Conda + JuiceFS :增强 AI 开发环境共享能力
Conda 是当前 AI 应用开发领域中非常流行的环境和包管理系统,因其能够简单便捷地创建与系统资源相隔离的虚拟环境广受欢迎。 Conda 支持在不同的操作系统上重建相同的工作环境,但在环境共享复用方面仍存在一些挑战。比如,在不同机器上复用相…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...