SpringBoot 使用【AOP 切面+注解】实现在请求调用 Controller 方法前修改请求参数和在结果返回之前修改返回结果

前情提要

在项目中需要实现 在请求调用 Controller 方法前修改请求参数和在结果返回之前修改返回结果

我们可以使用 AOP 切面+注解的形式实现。这样我们就可以在不修改原始代码的情况下,通过切面类在方法调用前后插入额外的逻辑。

解决方案

自定义注解 @PreProcess

自定义注解 @PreProcess 用于标记需要在方法调用前进行预处理的方法,以便后续处理。

@Target(ElementType.METHOD) // 表示该注解只能应用在方法上
@Retention(RetentionPolicy.RUNTIME) // 表示该注解在运行时仍然可用
public @interface PreProcess {// 可以添加其他注解属性的注释
}

自定义切面类 ModifiyAspect

接下来我们需要创建一个切面类,用于实现对带有自定义 @PreProcess 注解的方法进行处理。切面类代码如下:

/*** 用于对带有 @PreProcess 注解的方法进行处理的切面类。*/
@Aspect // 表示该类是切面类
@Component // 表示该类为 Spring 组件
@Order(1001) // 表示该切面类的优先级为 1001
public class ModifiyAspect {/*** 定义切入点,用于匹配带有 @PreProcess 注解的方法。*/@Pointcut("@annotation(com.example.demo.demos.annotation.PreProcess)")public void preProcess() {}/*** 在方法调用后进行处理,修改返回结果。* @param joinPoint 连接点对象* @return 修改后的返回结果* @throws Throwable 异常*/@Around("preProcess()")public Object modifyResult(ProceedingJoinPoint joinPoint) throws Throwable {// 1.获取方法返回结果Object result = joinPoint.proceed();// 2.修改返回结果JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(result));jsonObject.put("aspect", "aspect");// 3.返回修改后的结果result = jsonObject;return result;}/*** 在方法调用前进行处理,修改请求参数。* @param joinPoint 连接点对象* @return 修改后的返回结果* @throws Throwable 异常*/@Around("preProcess()")public Object modifyParam(ProceedingJoinPoint joinPoint) throws Throwable {// 1.获取请求参数Object[] args = joinPoint.getArgs();Object arg = args[0];// 2.修改请求参数JSONObject jsonData = JSONObject.parseObject(JSON.toJSONString(arg));jsonData.put("test", "test");// 3.返回修改后的请求参数args[0] = JSONObject.parse(JSON.toJSONString(jsonData));return  joinPoint.proceed(args);}}

在这个切面类中,通过对方法调用前后的处理,实现了对返回结果和请求参数的修改。

该类具体包含以下几个部分:

  1. 类注解:

    • @Aspect:表示该类是一个切面类,用于声明该类中包含了切面逻辑。
    • @Component:表示该类是 Spring 组件,用于让 Spring 容器自动扫描和管理该类。
    • @Order:表示该切面类的优先级,用于指定组件的加载顺序或切面的执行顺序,优先级高的切面类将先被执行,数值越小,优先级越高,默认值为 Integer.MAX_VALUE
  2. 方法注解:

    • @Pointcut:定义切入点,即匹配带有 @PreProcess 注解的方法。
    • @Around:表示该方法是一个环绕通知,在方法调用前后都会执行。
  3. 方法:

    • modifyResult() 方法在方法调用后,将返回结果转换为 JSON 对象,并添加一个名为 “aspect” 的属性,表示该返回结果是由该切面类修改的。

    • modifParam() 方法在方法调用前,将传入的请求参数转换为 JSON 对象,并添加一个名为 “test” 的属性,表示该请求参数是由该切面类修改的。

注:

joinPoint.getArgs() 方法返回一个 Object[] 数组,其中包含了方法调用时传入的所有参数。

这里假设方法调用时至少传入了一个参数,并且只取第一个参数进行处理。因此通过 args[0] 可以获取到第一个参数(索引为0)。

如果方法调用时没有传入参数,或者需要处理多个参数,可以根据实际情况进行修改。

编写 Controller 测试

创建两个接口,同样的逻辑,接收一个请求体参数 params,再将接收的参数以 JSON 格式返回:

@RestController
public class BasicController {/*** 处理 /hello 请求的方法* @param params 请求体参数,以键值对的形式传递* @return 经过转换后的 JSONObject 对象*/@PreProcess@PostMapping("/hello")public JSONObject hello(@RequestBody Map<String, Object> params) {return JSONObject.parseObject(JSON.toJSONString(params));}@PostMapping("/hello1")public JSONObject hello1(@RequestBody Map<String,Object> params) {return JSONObject.parseObject(JSON.toJSONString(params));}
}

启动项目,在 ApiFox 中分别以同样的请求参数发送 POST 请求调用 /hello/hello1 接口:

  • 请求参数:

    {"name": "hello","age": 20
    }
    
  • /hello 接口返回结果:

    {"test": "test","aspect": "aspect","name": "hello","age": 20
    }
    
  • /hello1 接口返回结果:

    {"name": "hello","age": 20
    }
    

复制多个 ModifiyAspect 切面模拟多切面同时修改请求体参数和返回结果,使用@Order注解指定执行顺序,测试结果如下:

{"test2": "test2",	//切面二"test": "test",	//切面一"aspect": "aspect",	//切面一"name": "hello","aspect2": "aspect2",	//切面二"age": 20
}

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

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

相关文章

vue3源码解析——ref和reactive定义响应式的区别

ref 和 reactive 是 Vue 3.0 中用于定义响应式数据的两个新 API。它们有以下区别&#xff1a; ref 定义单个响应式数据 数据类型可以是任意类型。它通常用于定义原始数据类型为响应式数据。返回一个响应式对象&#xff0c;该对象包含一个 .value 属性&#xff0c;可用于获取和设…

【全栈小5】我的创作纪念日

目录 前言机缘收获粉丝和原创个人成就六边形战士 回顾文章原代码代码优化 憧憬 前言 全栈小5 &#xff0c;有幸再次遇见你&#xff1a; 还记得 2019 年 03 月 29 日吗&#xff1f; 你撰写了第 1 篇技术博客&#xff1a; 《前端 - 仿动态效果 - 展开信息图标》 在这平凡的一天&…

SpringBoot -- Profiles

Profiles具备环境隔离能力&#xff0c;可以将我们的项目快速切换开发、测试、生产环境 我们的使用步骤也很简单&#xff1a; 1. 标识环境&#xff1a;指定哪些组件、配置在哪个环境生效 2. 切换环境&#xff1a;这个环境对应的所有组件和配置就应该生效 接下来就进行详细的…

蓝桥杯 付账问题

Problem: 蓝桥杯 付账问题 文章目录 思路解题方法复杂度Code 思路 这是一个关于付款问题的题目&#xff0c;我们需要找到一个最优的付款策略&#xff0c;使得每个人付款的金额尽可能接近平均值。我们可以通过排序和贪心的策略来解决这个问题。 解题方法 首先&#xff0c;我们将…

【JS】null和undefined有什么区别

前言 JS的作者Brendan Eich曾说过两者的区别&#xff1a; null means “no object”&#xff0c; undefined > “no value”.Really it’s an abstraction leak:null and objects shared a Mocha type tag. 翻译后&#xff1a; null 表示“没有对象”&#xff0c;undefined…

STM32学习笔记(9_3)- USART串口代码

无人问津也好&#xff0c;技不如人也罢&#xff0c;都应静下心来&#xff0c;去做该做的事。 最近在学STM32&#xff0c;所以也开贴记录一下主要内容&#xff0c;省的过目即忘。视频教程为江科大&#xff08;改名江协科技&#xff09;&#xff0c;网站jiangxiekeji.com 本期介…

html目录

标签列表 基础 <!DOCTYPE> &#xff1a;文档类型 <html>&#xff1a;HTML 文档 <title> &#xff1a;文档标题 <body>&#xff1a;文档主体 <h1> to <h6>&#xff1a;HTML 标题 <p> &#xff1a;段落 <br>&am…

Memcached 教程之Memcached介绍(一)

Memcached 教程 Memcached是一个自由开源的&#xff0c;高性能&#xff0c;分布式内存对象缓存系统。 Memcached是以LiveJournal旗下Danga Interactive公司的Brad Fitzpatric为首开发的一款软件。现在已成为mixi、hatena、Facebook、Vox、LiveJournal等众多服务中提高Web应用…

POSIX信号量

1.快速认识信号量接口 POSIX信号量和SystemV信号量作用相同&#xff0c;都是用于同步操作&#xff0c;达到无冲突的访问共享资源目的。 但POSIX可以用于线程间同步。我们之前认识SystemV信号量时有这样三个结论&#xff1a; 1.信号量的本质是一把计数器 2.申请信号量本质就是预…

进程调度算法

进程调度算法 进程调度算法先来先服务调度基于优先级调度&#xff08;Priority Scheduling&#xff09;短进程优先 / 最短剩余时间优先轮转法&#xff08;Round-Robin Scheduling&#xff09;高响应比优先调度算法&#xff08;Highest Response Ratio Next&#xff09;多级反馈…

word点保存图片模糊

在Word中&#xff0c;通过**点击左上角的“文件”按钮&#xff0c;选择“选项”&#xff0c;然后在“高级”选项中找到“图像大小和质量”&#xff0c;勾选【不压缩文件中的图像】选项&#xff0c;**以防止在保存文件时自动压缩图片。 如果已经保存了文档并且图片变得模糊&…

mysql 常见数据表操作

前面介绍了数据库表的基本操作。把常用的做一个汇总。时间久了&#xff0c;记不得完整的语法了&#xff0c;再打开一看&#xff0c;就清楚了。 先看注意事项&#xff1a; 在设计数据库的时候有以下注意点和技巧。 1&#xff0c;禁用存储过程、函数、触发器、外键约束&#xff…

jupyter 设置工作目录

本博客主要介绍&#xff1a; 如何为jupyter设置工作目录 1.打开 anaconda prompt , 执行 jupyter notebook --generate-config 执行这个命令后会生成一个配置文件 2. 打开jupyter_notebook_config.py文件编辑 搜索notebook_dir&#xff0c;把这行代码的注释取消&#xff0c;…

stm32再实现感应开关盖垃圾桶

一、项目需求 检测靠近时&#xff0c;垃圾桶自动开盖并伴随滴一声&#xff0c;2秒后关盖 发生震动时&#xff0c;垃圾桶自动开盖并伴随滴一声&#xff0c;2秒后关盖 按下按键时&#xff0c;垃圾桶自动开盖并伴随滴一声&#xff0c;2秒后关盖 硬件清单 SG90 舵机&#xff0c;…

HTTP 与 HTTPS 的区别

基本概念 HTTP&#xff08;HyperText Transfer Protocol&#xff1a;超文本传输协议&#xff09;是一种应用层协议&#xff0c;主要用于在网络上进行信息的传递&#xff0c;特别是用于Web浏览器和服务器之间的通信。 它使用明文方式发送数据&#xff0c;这意味着传输的内容可…

MySQL中MD5()函数加密CONCAT()函数拼接的字段

在MySQL中&#xff0c;使用CONCAT()函数来连接多个字段&#xff0c;然后对其结果应用MD5()函数进行加密。 SQL查询语句 UPDATE po_electricity SET task_code MD5(CONCAT(school_id, -, electricity_month, LOCKDATAV)) WHERE electricity_id 43;UPDATE po_water …

公司服务器被.rmallox攻击了如何挽救数据?

公司服务器被.rmallox攻击了如何挽救数据&#xff1f; .rmallox这种病毒与之前的勒索病毒变种有何不同&#xff1f;它有哪些新的特点或功能&#xff1f; .rmallox勒索病毒与之前的勒索病毒变种相比&#xff0c;具有一些新的特点和功能。这种病毒主要利用加密技术来威胁用户&am…

【JavaScript】数组 ③ ( JavaScript 数组长度 | 修改数组长度 | 数组案例 )

文章目录 一、JavaScript 数组长度1、数组长度2、修改数组长度 二、数组案例1、求数组元素平均值2、求数组元素最大值 一、JavaScript 数组长度 1、数组长度 在 JavaScript 中 , 数组长度 可以通过 数组变量的 length 属性 获取 , 该属性 返回 数组中的元素数量 , 也就是 数组长…

数据结构 第6章 图(一轮习题总结)

数据结构 第6章 图 6.1 图的基本概念6.2 图的存储及基本操作6.3 图的遍历6.4 图的应用 6.1 图的基本概念&#xff08;2 4 11&#xff09; 6.2 图的存储及基本操作&#xff08;1 12 13 15 16&#xff09; 6.3 图的遍历&#xff08;2 3 5 16&#xff09; 6.4 图的应用 6.1 图的基…

【USB】C#使用HID通信

最近做了一个USB通信SDK, 通过HID跟单片机通信&#xff0c;之前研究了一下Libusb, Cyusb, 要么死的太早&#xff0c;要么封装的不好&#xff0c;最后绕来绕去发现还是HID好用&#xff0c;反编译了一个SimpleHid, 别说&#xff0c;用起来还是很酸爽的~~~ 1.设备识别 首先你要指…