《学会 SpringMVC 系列 · 写入拦截器 ResponseBodyAdvice》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍

文章目录

    • 写在前面的话
    • ResponseBodyAdvice
      • 技术说明
      • 基础示例
      • 知识拓展
    • 源码知识回顾
    • 总结陈词

CSDN.gif

写在前面的话

前几篇博文,大致了解了SpringMVC请求流程中的参数与返回值的源码分析,后续的几篇博文,会将流程中涉及的若干关键环节单独拿出来讲解,并结合实战中的运用,帮助领略SpringMVC带来的定制和扩展能力。
本篇文章先介绍一下 ResponseBodyAdvice 相关内容。

相关博文
《学会 SpringMVC 系列 · 基础篇》
《学会 SpringMVC 系列 · 剖析篇(上)》
《学会 SpringMVC 系列 · 剖析入参处理》
《学会 SpringMVC 系列 · 剖析出参处理》
《学会 SpringMVC 系列 · 返回值处理器》
《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》


ResponseBodyAdvice

技术说明

0、ResponseBodyAdvice 是 Spring Framework 的 Web 模块中的一个接口,它允许你在将响应体写入 HTTP 响应之前拦截和修改它。它提供了一种全局定制响应处理逻辑的方式,适用于 Spring MVC 或 Spring WebFlux 应用程序。
1、ResponseBodyAdvice 可以在注解 @ResponseBody 将返回值处理成相应格式之前操作返回值,实现这个接口即可完成相应操作,可用于对response 数据的一些统一封装或者加密等操作。
2、ResponseBodyAdvice 接口和 RequestBodyAdvice 接口类似,RequestBodyAdvice 是请求到Controller 之前拦截,做相应的处理操作,而ResponseBodyAdvice 是对Controller返回的{@code @ResponseBody}or a {@code ResponseEntity} 后,{@code HttpMessageConverter} 类型转换之前拦截,进行相应的处理操作后,再将结果返回给客户端。
3、实现 ResponseBodyAdvice 接口,需要重写其 supports 和 beforeBodyWrite 方法。
1)supports方法:判断是否要执行beforeBodyWrite方法,true为执行,false不执行。通过该方法可以选择哪些类或那些方法的response要进行处理,其他的不进行处理。
2)beforeBodyWrite方法:对response方法进行具体操作处理。

public interface ResponseBodyAdvice<T> {/*** 1、选择是否执行 beforeBodyWrite 方法,返回 true 执行,false 不执行* 2、通过 supports 方法,可以选择对哪些类或方法的 Response 进行处理* @param returnType:返回类型* @param converterType:转换器* @return :返回 true 则下面的 beforeBodyWrite  执行,否则不执行*/boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);/*** 对 Response 处理的具体执行方法* @param body:响应对象(response)中的响应体* @param returnType:控制器方法的返回类型* @param selectedContentType:通过内容协商选择的内容类型* @param selectedConverterType:选择写入响应的转换器类型* @param request:当前请求* @param response:当前响应* @return :返回传入的主体或修改过的(可能是新的)主体*/@NullableT beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response);
}

基础示例

@ControllerAdvice
public class CustomResponseBodyAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType, Class converterType) {// 根据返回类型和转换器类型检查是否应用此建议// 你可以在这里放置任何条件return true;}@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType, ServerHttpRequest request,ServerHttpResponse response) {// 在将响应体写入输出流之前修改它// 你可以在这里检查或修改 'body' 对象return body;}
}

总结:ResponseBodyAdvice 接口允许在执行 @ResponseBody 或 ResponseEntity 控制器方法之后,但在使用 HttpMessageConverter 写入响应体之前自定义响应,进行功能增强。通常用于加密,签名,统一数据格式等。


知识拓展

【知识扩展1:与 HandlerMethodReturnValueHandler 区别】
sbdemo4 项目的返回值包装,使用自定义HandlerMethodReturnValueHandler实现,那两个都能实现,有什么区别呢?先参考一下下方GPT的回答。

HandlerMethodReturnValueHandler 和 ResponseBodyAdvice 都是 Spring MVC 中用于处理控制器方法返回值
的扩展点,但它们的功能和使用方式有所不同。HandlerMethodReturnValueHandler:
功能:HandlerMethodReturnValueHandler 用于处理控制器方法的返回值,并决定如何将返回值转换为响应。它负责控制器方法返回值的处理过程,例如将返回值转换为特定的响应格式(JSON、XML等)、对返回值进行处理、将返回值写入响应等。
使用方式:你可以通过实现 HandlerMethodReturnValueHandler 接口来定义自定义的返回值处理器,并将其注册到 Spring MVC 的配置中。你可以通过配置 WebMvcConfigurer 的 addReturnValueHandlers 方法来注册自定义的返回值处理器。ResponseBodyAdvice:
功能:ResponseBodyAdvice 用于在将响应返回给客户端之前对响应进行自定义处理。它允许你在响应体写入之前对响应进行修改、加密、压缩等操作。它不负责决定如何将控制器方法的返回值转换为响应,而是在返回值已经被转换为响应体后进行处理。
使用方式:你可以通过实现 ResponseBodyAdvice 接口来定义全局性的响应体处理逻辑,并将其注册到 Spring MVC 的配置中。你可以通过 @ControllerAdvice 或 @RestControllerAdvice 注解来标记全局的响应体处理器。总的来说,HandlerMethodReturnValueHandler 更加底层,负责控制器方法返回值的处理过程;
而 ResponseBodyAdvice 更加高层,用于在将响应返回给客户端之前对响应进行全局性的自定义处理。
通常情况下,你可以根据具体的需求选择合适的扩展点。

因为 SpringBoot 默认的 ResponseBody 的处理程序就是 HandlerMethodReturnValueHandler(具体是RequestResponseBodyMethodProcessor),所以我们自定义的通常 HandlerMethod 正常无法生效,非要使用HandlerMethod,那么只能替换掉默认的(放到第一个),如果只是想对Controller的所有返回值进行封装,产生上面的效果,使用ResponseBodyAdvice会更加简单一些,总之,改动不会那么大,尽量不要影响框架默认的实现。

【知识扩展2:关于返回值处理总结】
首先想到拦截器,HandlerInterceptor,其 postHandle 方法通常用于在控制器方法执行之后、视图渲染之前执行一些自定义逻辑,它并不直接提供获取返回值的功能。因为在 postHandle 方法被调用时,控制器方法的返回值已经被用于生成响应,拦截器只能对请求和响应进行处理,无法直接获取返回值。
要想处理返回值,可以有如下做法:
1、使用ResponseBodyAdvice:ResponseBodyAdvice 是一个用于全局性响应体处理的接口,在控制器方法返回值转换为响应体之前被调用,你可以在 beforeBodyWrite 方法中获取控制器方法的返回值。处理返回的数据在传递给HttpMessageConverter之前。
2、使用AOP切面:通过定义一个切面,在切面的方法中获取控制器方法的返回值,并进行相应的处理。通过切面拦截控制器方法的执行,你可以在方法执行完毕后获取返回值。
3、在拦截器的 postHandle 方法中,将返回值存储到请求属性中,然后在其他地方从请求属性中获取。这样虽然是间接的方式,但也可以实现获取返回值的目的。

public class MyInterceptor extends HandlerInterceptorAdapter {private ThreadLocal<Object> returnValueThreadLocal = new ThreadLocal<>();@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 在处理器执行后调用,但在视图渲染前调用System.out.println("Post-handle method is called");// 在这里获取控制器方法的返回值,并存储到ThreadLocal变量中Object returnValue = modelAndView != null ? modelAndView.getModel().get("handlerReturnValue") : null;returnValueThreadLocal.set(returnValue);}// 提供一个公共方法,供其他组件调用获取返回值public Object getControllerReturnValue() {return returnValueThreadLocal.get();}
}

【知识扩展3:@ControllerAdvice】
ResponseBodyAdvice 自定义使用过程中,加上了@ControllerAdvice注解,有什么用?
顾名思义,@ControllerAdvice就是@Controller 的增强版。@ControllerAdvice主要用来处理全局数据,一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用。
这里不展开介绍,框架的 GlobalExceptionHandler 全局异常处理就是用了这个方式。
1、在使用ResponseBodyAdvice时,通常需要将其标注为@ControllerAdvice注解的类,该注解用于定义全局的控制器增强,可以对所有的Controller进行统一的处理,当我们在@ControllerAdvice注解的类中实现ResponseBodyAdvice接口时,就可以对所有Controller方法返回的响应体进行统一处理;
2、经测试,不添加@ControllerAdvice注解,或仅使用@Component注解,功能都是无效的;

【知识扩展4:@ControllerAdvice 和 @RestControllerAdvice 的区别】
@ControllerAdvice 和 @RestControllerAdvice 都是 Spring 中用于定义全局异常处理、数据绑定、模型数据处理的注解。两者的主要区别在于它们如何处理控制器的返回值。
@ControllerAdvice 是一个更通用的注解,用于为控制器提供全局的异常处理、数据绑定等功能。它适用于处理所有类型的控制器,包括返回视图名称的控制器。使用这个注解时,返回的对象通常是一个视图名称,或者是包含视图名称和模型数据的 ModelAndView 对象。

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(Exception.class)public String handleException(Exception ex) {return "error"; // 返回视图名称}
}

@RestControllerAdvice 是 @ControllerAdvice 的一个特化版本,专门用于 RESTful Web 服务。它与 @ControllerAdvice 的区别在于,它隐式地在类的每个方法上添加了 @ResponseBody 注解。因此,返回的对象会自动序列化为 JSON 或 XML 等格式,写入响应体。

@RestControllerAdvice
public class GlobalRestExceptionHandler {@ExceptionHandler(Exception.class)public ErrorResponse handleException(Exception ex) {return new ErrorResponse("Error occurred", ex.getMessage()); // 返回 JSON 对象}
}

@ControllerAdvice@RestControllerAdvice 用于实现 ResponseBodyAdvice 时,两者的行为是相同的,都会在响应体写入前提供一个拦截点,无论返回类型是 ViewModelAndView 还是 JSON、XML 等。

Tips:区别其实有点类似@RestController和@Controller的区别,


源码知识回顾

本篇为 SpringMVC 源码分析系列文章,正片开始前,先总结回顾一下全流程。

【一次请求的主链路节点】
DispatcherServlet#doDispatch(入口方法)
DispatcherServlet#getHandler(根据path找到对应的HandlerExecutionChain
DispatcherServlet#getHandlerAdapter(根据handle找到对应的HandlerAdapter
HandlerExecutionChain#applyPreHandle(触发拦截器的前置逻辑)
AbstractHandlerMethodAdapter#handle(核心逻辑)
HandlerExecutionChain#applyPostHandle(触发拦截器的后置逻辑)

【核心handle方法的主链路节点】
RequestMappingHandlerAdapter#handleInternal(入口方法)
RequestMappingHandlerAdapter#invokeHandlerMethod(入口方法2)
ServletInvocableHandlerMethod#invokeAndHandle(入口方法3)
InvocableHandlerMethod#invokeForRequest(参数和实际执行的所在,3.1)
InvocableHandlerMethod#getMethodArgumentValues(参数处理,3.1.1)
InvocableHandlerMethod#doInvoke(实际执行,3.1.2)
HandlerMethodReturnValueHandlerComposite#handleReturnValue(返回处理,3.2)
image.png

【针对 @RequestBody 和 @ResponseBody 场景】
image.png


总结陈词

本篇博文继请求源码分析后,继续介绍了ResponseBodyAdvice的用法,无独有偶,请求阶段也有对应的RequestBodyAdvice,而且读取前和读取后,都可以自定义扩展,欲知后事如何,请听下回分解。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

CSDN_END.gif

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

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

相关文章

MATLAB画散点密度图(附代码和测试数据的压缩包)

1. 有关 Matlab 获取代码关注WZZHHH回复关键词&#xff0c;或者咸鱼关注&#xff1a;WZZHHH123 怀俄明探空站数据解算PWV和Tm&#xff1a;怀俄明探空站数据解算PWV和Tm 怀俄明多线程下载探空站数据&#xff08;包括检查和下载遗漏数据的代码&#xff09;&#xff1a;怀俄明多线…

VMware安装Win10系统(保姆级教程)

需要自己先下载并安装VMware 和Win10系统镜像&#xff1a; VMware官网&#xff1a;VMware - Delivering a Digital Foundation For Businesses Win10下载地址&#xff1a;MSDN,我告诉你 1.新建虚拟机设置 2.启动Win10虚拟机设置 注意&#xff1a; 当出现有字体的时候&#…

一款绿色免费免安装的hosts文件编辑器

BlueLife Hosts Editor 是一款免费的 Hosts 文件编辑工具&#xff0c;主要用于管理和修改电脑系统的 Hosts 文件。该软件具有多种功能&#xff0c;包括添加、删除和更新域名记录&#xff0c;以及调整 IP 与网域名称的交叉对应关系&#xff0c;类似于 DNS 的功能。 该软件特别适…

filebeat

1、作用 1、可以在本机收集日志2、也可以远程收集日志3、轻量级的日志收集系统&#xff0c;可以在非java环境运行。logstash是在jmv环境中运行&#xff0c;资源消耗很大&#xff0c;启动一个logstash要消耗500M左右的内存&#xff0c;filebeat只消耗10M左右的内存。收集nginx的…

Qt 学习第四天:信号和槽机制(核心特征)

信号和槽的简介 信号和插槽用于对象之间的通信。信号和插槽机制是Qt的核心特征&#xff0c;可能是不同的部分大部分来自其他框架提供的特性。信号和槽是由Qt的元对象系统实现的。介绍&#xff08;来自Qt帮助文档Signals & Slots&#xff09; 在GUI编程中&#xff0c;当我们…

使用 Rough.js 创建动态可视化网络图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 Rough.js 创建动态可视化网络图 应用场景 Rough.js 是一个 JavaScript 库&#xff0c;它允许开发人员使用毛边风格创建可视化效果。该库适用于各种应用程序&#xff0c;例如&#xff1a; 数据可视化地图…

计算机基础(Windows 10+Office 2016)教程 —— 第8章 多媒体技术及应用

多媒体技术及应用 8.1 多媒体技术的概述8.1.1 多媒体技术的定义和特点8.1.2  多媒体的关键技术8.1.3 多媒体技术的发展趋势8.1.4 多媒体文件格式的转换8.1.5 多媒体技术的应用 8.2 多媒体计算机系统的构成8.2.1 多媒体计算机系统的硬件系统8.2.2 多媒体计算机系统的软件系统…

APP逆向 day26unidbg下-pdd(anti)案例

一.前言 今天我们讲unidbg的下篇&#xff0c;也就是unidbg基础的最后一个部分&#xff0c;我们上节课也有补环境&#xff0c;比如补java环境&#xff0c;补安卓环境&#xff0c;这节课我们讲的肯定比这些都要难&#xff0c;我会给出一个之前讲过的案例&#xff0c;然后会讲一个…

多 NodeJS 环境管理

前言 对于某个项目依赖特定版本的 NodeJS&#xff0c;或几个项目的 NodeJS 版本冲突时&#xff0c;需要在系统中安装多个版本的 NodeJS&#xff0c;这时可以使用一些工具来进行多个 NodeJS 的管理。 有很多类似的 NodeJS 管理工具&#xff0c;如 nvm, nvs, n 等&#xff0c;接…

深入理解单元测试与JUnit:从基础概念到实践操作

文章目录 前言一、单元测试是什么&#xff1f;单元测试的特点单元测试的好处 二、junit是什么&#xff1f;三、操作步骤1.junit安装2.maven新建项目3. 新建java文件4. 生成测试类5. 编写测试方法6. 测试结果 总结 前言 随着软件开发行业的不断发展&#xff0c;测试的重要性日益…

C++自定义接口类设计器之函数解析二

关键代码 // 解析为函数 bool FunctionCreator::parse(const QString& lineFunc) {auto trimFunc lineFunc.trimmed();auto list trimFunc.split(" ");bool bHasReturn false;// 返回值和函数名解析for (const auto& key : list) {auto trimKey key.trim…

串口应用编程-I.MX6U嵌入式Linux C应用编程学习笔记基于正点原子阿尔法开发板

串口应用编程 串口应用编程介绍 介绍 串口定义:串行接口,数据按顺序传输 串口特点:通信线路简单,距离远,速度较低 应用领域:常用工业接口 Linux系统中的作用 作为标准输入输出设备 系统打印信息输出 用户与系统交互 串口与终端:在Linux系统中,串口被视为一种终端&#…

2024年软件测试岗必问的100+个面试题【含答案】

一、基础理论 1、开场介绍 介绍要领&#xff1a;个人基本信息、工作经历、之前所做过的工作及个人专长或者技能优势。扬长避短&#xff0c;一定要口语化&#xff0c;语速适中。沟通好的就多说几句&#xff0c;沟通不好的话就尽量少说两句。举例如下&#xff1a; 面试官你好&…

前端Web-JavaScript(下)

主要是补全一下JavaScript 基本对象: String对象 语法格式 方式1&#xff1a; var 变量名 new String("…") ; //方式一 例如&#xff1a; var str new String("Hello String"); 方式2&#xff1a; var 变量名 … ; //方式二 例如&#xff1a; var …

【外排序】--- 文件归并排序的实现

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; 数据结构 我们之前学习的八大排序&#xff1a;冒泡&#xff0c;快排&#xff0c;插入&#xff0c;堆排等都是内排序&#xff0c;这些排序算法处理的都是…

一键生成视频并批量上传视频抖音、bilibili、腾讯(已打包)

GenerateAndAutoupload Github地址&#xff1a;https://github.com/cmdch2017/GenerateAndAutoupload 如何下载&#xff08;找到最新的release&#xff09; https://github.com/cmdch2017/GenerateAndAutoupload/releases/download/v1.0.1/v1.0.1.zip 启动必知道 conf.py …

数论第四节:二元一次不定方程、勾股数

不定方程定义 解不确定的方程称为不定方程。一般化的定义为&#xff1a;不定方程是指未知数的个数多余方程的个数&#xff0c;或未知数受到某种限制&#xff08;如整数、正整数等&#xff09;的方程和方程组。 二元一次不定方程定义 形如axbyc的形式的方程。其中a,b不等于0&…

Qt对象树的介绍

目录 创建项目&#xff08;此处我就不多介绍了&#xff09; 按钮 对象树 创建项目&#xff08;此处我就不多介绍了&#xff09; QMainWidow带菜单栏的 QWidget空白的 QDialog对话框 创建功能时注意&#xff1a; 项目工程名称一般不要有标点&#xff0c;不要带中文 按钮 /…

变量作用域、作用域链、return

全局变量 全局变量因为在全局操作会每次留存上次操作的结果 局部变量因为执行完成就会被销毁并不会保留本次操作的结果 可以通过传参和返回&#xff0c;将结果不断地专递处理 局部变量 参数也是局部变量 函数内的预解析预赋值 函数内的局部变量 如果同名全局变量遇到局部变量…

linux进程控制——进程替换——exec函数接口

前言&#xff1a; 本节内容进入linux进程控制板块的最后一个知识点——进程替换。 通过本板块的学习&#xff0c; 我们了解了进程的基本控制方法——进程创建&#xff0c; 进程退出&#xff0c; 进程终止&#xff0c; 进程替换。 进程控制章节和上一节进程概念板块都是在谈进程…