Spring AOP实现切入增强的两种方式(execution+annotation)-Demo
pom文件依赖
<!-- AOP切面编程启动环境依赖组 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1、通过execution表达式实现切入增强
package com.xch.aop_execution.advicetype;import org.springframework.stereotype.Component;/*** 计算业务实现类** @author XuChenghe* @date 2023/8/19 17:43*/
@Component
public class Count {/*** 正常计算方法** @param num1* @param num2* @return*/public Integer normal(Integer num1, Integer num2) {System.out.println("-----执行正常计算方法的逻辑(开始)-----");int x = 1 / 1;System.out.println("-----执行正常计算方法的逻辑(结束)-----");return num1 + num2;}/*** 异常计算方法** @param num1* @param num2* @return*/public Integer abnormal(Integer num1, Integer num2) {System.out.println("-----执行异常计算方法的逻辑(开始)-----");int x = 1 / 0;System.out.println("-----执行异常计算方法的逻辑(结束)-----");return num1 - num2;}}
package com.xch.aop_execution.advicetype;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;import java.util.Arrays;/*** 计算业务的切面类:测试通知类型* -其中,后置通知/返回通知 和 最终通知的执行顺序,会根据Spring的版本而异** @author XuChenghe* @date 2023/8/19 17:41*/
@Aspect // 声明该类的作用:切面类
@Component // 等价于@Configuration:交由Spring的IOC容器管理
public class CountAspect {/*** 定义公共切入点* execution表达式配置规则:* -execution([修饰符] 返回类型 方法全限定名称(参数) [异常])* -其中修饰符和异常可选* -参数..代表任意入参* -*代表任意返回类型或任意方法名* -*xxx/xxx*代表以xxx结尾或开头的任意方法名* -xxx.*代表xxx类下的所有方法或xxx包下所有类的所有方法* -xxx..*代表xxx包下所有包或类的所有方法*/@Pointcut("execution(* com.xch.aop_execution.advicetype.Count.*(..))")public void myPointcut() {// 公共切入点}/*** 前置通知** @param joinPoint 连接点:当前切入方法的信息*/@Before("myPointcut()")public void before(JoinPoint joinPoint) {System.out.println("=====前置通知(开始):执行安全检查逻辑=====");// 前置通知,可以拿到方法名称+方法入参String name = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();System.out.println("name = " + name);System.out.println("args = " + Arrays.toString(args));System.out.println("=====前置通知(结束):执行安全检查逻辑=====");}/*** 后置通知/返回通知* 只有目标方法中没有抛出异常,即正常执行返回结果后,才进入该通知** @param joinPoint 连接点:当前切入方法的信息* @param result 方法执行的返回结果*/@AfterReturning(pointcut = "myPointcut()", returning = "result")public void afterReturning(JoinPoint joinPoint, Object result) {System.out.println("=====后置通知/返回通知(开始):记录日志=====");// 后置通知/返回通知,可以拿到方法名称+方法入参+方法返回值String name = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();System.out.println("name = " + name);System.out.println("args = " + Arrays.toString(args));System.out.println("result = " + result);System.out.println("=====后置通知/返回通知(结束):记录日志=====");}/*** 最终通知* 不管目标方法中有没有抛出异常,都会进入该通知** @param joinPoint 连接点:当前切入方法的信息*/@After("myPointcut()")public void after(JoinPoint joinPoint) {System.out.println("=====最终通知(开始):释放资源=====");// 最终通知,可以拿到方法名称+方法入参String name = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();System.out.println("name = " + name);System.out.println("args = " + Arrays.toString(args));System.out.println("=====最终通知(结束):释放资源=====");}/*** 环绕通知* 该通知等价于其余四种通知的大集合* 执行了被切入的方法代码,改变了原执行路径,需返回执行结果** @param pjp 连接点:当前切入方法的信息* @return 方法执行的返回结果* @throws Throwable 方法执行时抛出的异常*/@Around("myPointcut()")public Object around(ProceedingJoinPoint pjp) throws Throwable {System.out.println("=====环绕通知(开始):手动开启事务=====");// 环绕通知,可以拿到方法名称+方法入参+方法返回值String name = pjp.getSignature().getName();Object[] args = pjp.getArgs();Object proceed = pjp.proceed();System.out.println("name = " + name);System.out.println("args = " + Arrays.toString(args));System.out.println("proceed = " + proceed);System.out.println("=====环绕通知(结束):手动提交事务=====");return proceed;}/*** 抛出异常后通知* 不能在Around环绕通知中捕获异常,否则不会给Spring AOP处理进入该通知** @param joinPoint 连接点:当前切入方法的信息* @param exception 方法执行时抛出的异常*/@AfterThrowing(pointcut = "myPointcut()", throwing = "exception")public void afterThrowing(JoinPoint joinPoint, Exception exception) {System.out.println("=====抛出异常后通知(开始):手动回滚事务=====");// 抛出异常后通知,可以拿到方法名称+方法入参+抛出的异常String name = joinPoint.getSignature().getName();Object[] args = joinPoint.getArgs();System.out.println("name = " + name);System.out.println("args = " + Arrays.toString(args));exception.printStackTrace();System.out.println("=====抛出异常后通知(结束):手动回滚事务=====");}}
package com.xch.aop_execution.advicetype;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;/*** 计算业务测试类** @author XuChenghe* @date 2023/8/19 18:17*/
@SpringBootTest
public class CountTest {@Autowiredprivate Count count;@Testpublic void test() {count.normal(3, 8);System.out.println();count.abnormal(8, 3);}}
2、通过annotation注解实现切入增强
package com.xch.aop_annotation.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 用户信息实体类** @author XuChenghe* @date 2023/8/19 12:37*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {/*** 用户ID*/private Integer id;/*** 用户名称*/private String name;}
package com.xch.aop_annotation.controller;import com.xch.aop_annotation.annotation.LogAnnotation;
import com.xch.aop_annotation.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Arrays;
import java.util.List;/*** 用户信息管理接口层实现类** @author XuChenghe* @date 2023/8/19 12:35*/
@RestController
@RequestMapping("/user")
public class UserController {/*** 获取所有用户信息** @return*/@GetMapping("/getUserList")public List<User> getUserList() {// 模拟业务的数据库操作return Arrays.asList(new User(1, "Alice"),new User(2, "Bob"),new User(3, "Lucy"));}/*** 通过用户ID获取用户信息** @param id* @return*/@GetMapping("/getUserById/{id}")public User getUserById(@PathVariable("id") Integer id) {// 模拟业务的数据库操作return new User(1, "Alice");}/*** 通过用户名称获取用户信息** @param name* @return*/@GetMapping("/getUserByName/{name}")public User getUserByName(@PathVariable("name") String name) {// 模拟业务的数据库操作return new User(2, "Bob");}/*** 保存用户信息** @return*/@GetMapping("/saveUser")@LogAnnotation("保存用户信息")public String saveUser() {// 模拟业务的数据库操作return "添加用户信息成功!";}/*** 编辑用户信息** @return*/@GetMapping("/editUser")@LogAnnotation("编辑用户信息")public String editUser() {// 模拟业务的数据库操作return "编辑用户信息成功!";}/*** 通过用户ID删除用户信息** @param id* @return*/@GetMapping("/deleteUserById/{id}")@LogAnnotation("通过用户ID删除用户信息")public String deleteUserById(@PathVariable("id") Integer id) {// 模拟业务的数据库操作return "通过用户ID删除用户信息成功!";}/*** 通过用户名称删除用户信息** @param name* @return*/@GetMapping("/deleteUserByName/{name}")@LogAnnotation("通过用户名称删除用户信息")public String deleteUserByName(@PathVariable("name") String name) {// 模拟业务的数据库操作return "通过用户名称删除用户信息成功!";}}
package com.xch.aop_annotation.annotation;import java.lang.annotation.*;/*** 记录写操作方法日志的注解** @author XuChenghe* @date 2023/8/19 12:57*/
@Target(ElementType.METHOD) // 声明该注解作用的地方:方法
@Retention(RetentionPolicy.RUNTIME) // 声明该注解作用的时间:运行时
@Documented // 以上三个都是元注解:描述注解的注解
public @interface LogAnnotation {/*** 方法的作用描述*/String value() default "暂无作用描述(默认)";}
package com.xch.aop_annotation.aspect;import com.xch.aop_annotation.annotation.LogAnnotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Arrays;/*** 记录写操作方法日志的的切面类** @author XuChenghe* @date 2023/8/19 13:06*/
@Aspect // 声明该类的作用:切面类
@Component // 等价于@Configuration:交由Spring的IOC容器管理
public class LogAspect {/*** 定义公共切入点*/@Pointcut("@annotation(com.xch.aop_annotation.annotation.LogAnnotation)")public void logPointCut() {// 公共切入点}/*** 切面的执行方法(环绕通知):记录日志操作的代码** @param pjp 连接点:当前切入方法的信息* @return 方法执行的返回结果*/@Around("logPointCut()")public Object around(ProceedingJoinPoint pjp) throws Throwable {// 模拟业务的记录日志到数据库操作// 当前切入的方法名称String name = pjp.getSignature().getName();// 当前切入的方法返回值(执行了被切入的方法代码,改变了原执行路径,需返回执行结果)Object proceed = pjp.proceed();// 当前切入的方法入参Object[] args = pjp.getArgs();// 获取注解的属性内容// 1.通过反射反向获取方法MethodSignature signature = (MethodSignature) pjp.getSignature();Method method = signature.getMethod();// 2.通过反射反向获取注解LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class);if (logAnnotation != null) {String value = logAnnotation.value();System.out.println("========================================");System.out.println("name = " + name);System.out.println("proceed = " + proceed);System.out.println("args = " + Arrays.toString(args));System.out.println("value = " + value);System.out.println("========================================");}return proceed;}}相关文章:
Spring AOP实现切入增强的两种方式(execution+annotation)-Demo
pom文件依赖 <!-- AOP切面编程启动环境依赖组 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId> </dependency> 1、通过execution表达式实现切入增强 package com…...
人工智能在网络安全中的作用:当前的局限性和未来的可能性
人工智能 (AI) 激发了网络安全行业的想象力,有可能彻底改变安全和 IT 团队处理网络危机、漏洞和勒索软件攻击的方式。 然而,对人工智能的能力和局限性的现实理解至关重要,并且存在许多挑战阻碍人工智能对网络安全产生直接的变革性影响。 在…...
BC99 序列中整数去重
描述 输入n个整数的序列,要求对这个序列进行去重操作。所谓去重,是指对这个序列中每个重复出现的整数,只保留该数第一次出现的位置,删除其余位置。 输入描述 输入包含两行,第一行包含一个正整数n(1 ≤ n…...
[PyTorch][chapter 52][迁移学习]
前言: 迁移学习(Transfer Learning)是一种机器学习方法,它通过将一个领域中的知识和经验迁移到另一个相关领域中,来加速和改进新领域的学习和解决问题的能力。 这里面主要结合前面ResNet18 例子,详细讲解一…...
Ceph如何操作底层对象数据
1.基本原理介绍 1.1 ceph中的对象(object) 在Ceph存储中,一切数据最终都会以对象(Object)的形式存储在硬盘(OSD)上,每个的Object默认大小为4M。 通过rados命令,可以查看一个存储池中的所有object信息,例如…...
sklearn机器学习库(二)sklearn中的随机森林
sklearn机器学习库(二)sklearn中的随机森林 集成算法会考虑多个评估器的建模结果,汇总之后得到一个综合的结果,以此来获取比单个模型更好的回归或分类表现。 多个模型集成成为的模型叫做集成评估器(ensemble estimator)…...
FlutterBoost 实现Flutter页面内嵌iOS view
在使用Flutter混合开发中会遇到一些原生比Flutter优秀的控件,不想使用Flutter的控件,想在Flutter中使用原生控件。这时就会用到 Flutter页面中内嵌 原生view,这里简单介绍一个 内嵌 iOS 的view。 注:这里使用了 FlutterBoost。网…...
走嵌入式还是纯软件?学长告诉你怎么选
最近有不少理工科的本科生问我,未来是走嵌入式还是纯软件好,究竟什么样的同学适合学习嵌入式呢?在这里我整合一下给他们的回答,根据自己的经验提供一些建议。 嵌入式领域也可以分为单片机方向、Linux方向和安卓方向。如果你的专业…...
【云计算原理及实战】初识云计算
该学习笔记取自《云计算原理及实战》一书,关于具体描述可以查阅原本书籍。 云计算被视为“革命性的计算模型”,因为它通过互联网自由流通使超级计算能力成为可能。 2006年8月,在圣何塞举办的SES(捜索引擎战略)大会上&a…...
Open3D (C++) 基于拟合高差的点云地面点提取
目录 一、算法原理1、原理概述2、参考文献二、代码实现三、结果展示1、原始点云2、提取结果四、相关链接系列文章(连载中。。。): Open3D (C++) 基于高程的点云地面点提取Open3D (C++) 基于拟合平面的点云地面点提取Open3D (C++) 基于拟合高差的点云地面点提取</...
认识Transformer:入门知识
视频链接: https://www.youtube.com/watch?vugWDIIOHtPA&listPLJV_el3uVTsOK_ZK5L0Iv_EQoL1JefRL4&index60 文章目录 Self-Attention layerMulti-head self-attentionPositional encodingSeq2Seq with AttentionTransformerUniversal Transformer Seq2Seq …...
《TCP IP网络编程》第二十四章
第 24 章 制作 HTTP 服务器端 24.1 HTTP 概要 本章将编写 HTTP(HyperText Transfer Protocol,超文本传输协议)服务器端,即 Web 服务器端。 理解 Web 服务器端: web服务器端就是要基于 HTTP 协议,将网页对…...
【AI】文心一言的使用
一、获得内测资格: 1、点击网页链接申请:https://yiyan.baidu.com/ 2、点击加入体验,等待通过 二、获得AI伙伴内测名额 1、收到短信通知,点击链接 网页Link:https://chat.baidu.com/page/launch.html?fa&sourc…...
CSAPP Lab2:Bomb Lab
说明 6关卡,每个关卡需要输入相应的内容,通过逆向工程来获取对应关卡的通过条件 准备工作 环境 需要用到gdb调试器 apt-get install gdb系统: Ubuntu 22.04 本实验会用到的gdb调试器的指令如下 r或者 run或者run filename 运行程序,run filename就…...
Java中使用流将两个集合根据某个字段进行过滤去重?
Java中使用流将两个集合根据某个字段进行过滤去重? 在Java中,您可以使用流(Stream)来过滤和去重两个集合。下面是一个示例代码,展示如何根据对象的某个字段进行过滤和去重操作: import java.util.ArrayList; import java.util.List; impor…...
自动驾驶HMI产品技术方案
版本变更 序号 日期 变更内容 编制人 审核人 文档版本 1 2 1....
Git判断本地是否最新
场景需求 需要判断是否有新内容更新,确定有更新之后执行pull操作,然后pull成功之后再将新内容进行复制到其他地方 pgit log -1 --prettyformat:"%H" HEAD -- . "origin/HEAD" rgit rev-parse origin/HEAD if [[ $p $r ]];thenecho "Is La…...
Spring 整合RabbitMQ,笔记整理
1.创建生产者工程 spring-rabbitmq-producer 2.pom.xml添加依赖 <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.7.RELEASE</version></dep…...
Lua 语言笔记(一)
1. 变量命名规范 弱类型语言(动态类型语言),定义变量的时候,不需要类型修饰 而且,变量类型可以随时改变每行代码结束的时候,要不要分号都可以变量名 由数字,字母下划线组成,不能以数字开头,也不…...
【Redis】什么是缓存穿透,如何预防缓存穿透?
【Redis】什么是缓存穿透,如何预防缓存穿透? 缓存穿透是指查询一个一定不存在的数据,由于缓存中不存在,这时会去数据库查询查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,这…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例
一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
