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

news2024/9/24 15:24:07

📢 大家好,我是 【战神刘玉栋】,有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 :返回传入的主体或修改过的(可能是新的)主体
     */
	@Nullable
	T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response);
}

基础示例

@ControllerAdvice
public class CustomResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        // 根据返回类型和转换器类型检查是否应用此建议
        // 你可以在这里放置任何条件
        return true;
    }

    @Override
    public 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<>();

    @Override
    public 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.coloradmin.cn/o/1976177.html

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

相关文章

3.4数组和特殊矩阵

3.4.1数组的定义 数组是由n个相同类型的数据元素构成的有序序列 数组是线性表的推广,一个数组可以视为一个线性表 数组一旦被定义,其长度不会再改变,所以数组只会有存取元素和修改元素的操作 3.4.2数组的存储结构 多维数组 有两种映射方法:按行优先和按列优先 按行优先 …

2024 年最值得阅读的 10 个外国技术网站

从网络上数以千计的博客中挑选出最好的技术网站&#xff0c;并根据相关性、权威性、社交媒体关注者和新鲜度进行排名。 1. TechCrunch TechCrunch 是一家领先的科技媒体&#xff0c;致力于深入分析初创公司、评论新的互联网产品和发布科技新闻。该网站是科技专业人士和爱好者…

【传知代码】实体关系抽取(论文复现)

当谈论信息提取领域的最前沿时&#xff0c;实体关系抽取无疑是其中一颗耀眼的明星。从大数据时代的信息海洋中提炼出有意义的关系&#xff0c;不仅是科技进步的体现&#xff0c;更是人类对知识管理和智能决策迫切需求的响应。本文将探索实体关系抽取的核心技术、应用场景及其在…

域控搭建(windows 2012 R2和win10)

域控搭建 环境准备 两台windows虚拟机 主域控为&#xff1a;windows server2012 子域为&#xff1a;win10 虚拟机设置网段 Win10网络设置 Windows server2012网络设置 Windows server2012网络适配器 设置 识别成功 更改计算机名字 等待重启 Win10网络适配器 设置 识别成功 …

opencv-图像透视变换

透射变换是视角变化的结果&#xff0c;是指利用透视中心&#xff0c;像点&#xff0c;目标点共线的条件&#xff0c;按透视旋转定律使承影面(透视面)绕迹线(透视轴旋转某一角度&#xff0c;破坏原有的投影光束&#xff0c;仍能保持承影面上投影几何图形不变的变化) 它的本质将图…

QT实现步进电机控制和IMU数据读取显示

实现功能&#xff1a; 1.两步进电机分别使能和循环运动&#xff0c;可以设置循环次数、循环里分别运行的角度、旋转的速度和加减速度等等&#xff0c;在最下方的表格里显示发送和接收的CAN报文 2.读取水平电机当前位置和速度并画图显示&#xff0c;示波器暂停、缩放、滑动等功…

CVPR24《Neural Markov Random Field for Stereo Matching》

论文地址&#xff1a; https://arxiv.org/abs/2403.11193 源码地址&#xff1a; https://github.com/aeolusguan/NMRF 概述 手工设计的MRF模型在传统的立体匹配中占据主导地位&#xff0c;但与端到端的深度学习模型相比&#xff0c;其建模准确性不足。尽管深度学习大大改进了MR…

力扣SQL50 修复表中的名字 字符串函数

Problem: 1667. 修复表中的名字 &#x1f468;‍&#x1f3eb; 参考题解 select user_id, CONCAT(UPPER(left(name, 1)), LOWER(RIGHT(name, length(name) - 1))) as name from Users order by user_id

SQL注入实例(sqli-labs/less-2)

0、初始网页 1、闭合方式判断 当没有闭合符号进行注释时&#xff0c;网页并没有报错&#xff0c;所以可以确定无闭合符号&#xff0c;为数值型注入 2、确定查询表的列数 可以确定列数小于4 ?id1 order by 4 -- 确定查询表的列数为3列 ?id1 order by 3 -- 3、确定回显位置…

MySQL系列之--关系型数据库以及SQL语句分类之DDL数据库和表的操作

文章目录 前言关系型数据库&#xff08;RDBMS&#xff09;关系型数据库的特点 MySQL数据模型SQL介绍基本语法规则SQL语句的分类DDL的介绍DDL的数据库操作DDL的表操作 前言 上一节MySQL系列之–详细安装教程和启动方法中介绍了MySQL如何安装&#xff0c;以及如何启动和客户端连接…

c++| c++11左右值引用,完美转发,可变参数模板,functional包装器,bind函数

c| c11的新特性 左&#xff0c;右值引用什么是左值&#xff0c;右值左值引用和右值引用右值引用解决什么问题呢&#xff1f;移动构造万能引用形式 完美转发格式 lambada表达式格式 可变参数模板可变参数模板实现打印不同类型emplace_push以list的emplace_back的实现举例包装器b…

新160个crackme - 020-cosh.3

运行分析 老规矩&#xff0c;需要破解Name和Serial PE分析 c程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida查找关键字符串 分析关键函数&#xff0c;得出以下结论&#xff1a;Name、Serial每一位进行亦或计算&#xff0c;若计算结果相等则弹窗成功 算法分析…

吴恩达机器学习作业-ex7(主成分分析)

data1 导入库&#xff0c;读取数据&#xff0c;并进行可视化数据 import numpy as np import scipy.io as sio import matplotlib.pyplot as plt#读取数据 path "./ex7data1.mat" data sio.loadmat(path) # print(data.keys()) X data.get("X") # pri…

Python数据分析案例58——热门游戏数据分析及其可视化

案例背景 有哪个男生不喜欢玩游戏呢&#xff1f;就算上了班儿也要研究一下游戏以及热门的游戏。正好这里有个热门的游戏数据集&#xff0c;全球热门游戏数据集来做一下一些可视化的分析。 数据介绍 该文件包含一个数据集&#xff0c;详细说明了多个平台上的各种流行游戏。每个…

【Golang 面试 - 进阶题】每日 3 题(十五)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/UWz06 &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 Golang 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏…

Cesium 高德地图暗黑化

Cesium 高德地图暗黑化 高德电子地图实现暗黑效果 // 设置图层滤镜new ImageryLayerFilter({viewer: viewer,imageryLayer: imageryLayer,});

YOLOv5轻量化改进 | backbone | 结合MobileNetV4(包含多个结构和使用方式)

YOLOv5轻量化改进 | backbone | 结合MobileNetV4(包含多个结构) 本文介绍论文原理介绍网络代码多种yaml设置网络测试及实验结果<!-- 这里放入论文图片 --> &emsp;;本文介绍 本文给大家带来的改进机制是结合MobileNetV4骨干网络,其中来自2024.5月发布的MobileNetV4…

Pageadmin 漏洞教程

上传文件解压拿webshell 用哥斯拉&#xff0c;生成一个asp木马 这个就是我们生成的asp木马 不能直接上传 压缩为zip文件 上传 上传以后我们点击解压 就成功了 然后我们去访问 然后用哥斯拉访问 然后再点击添加 这样就成功了 成功进入

VSCode在windows系统下的配置简单版

参考链接 从零开始的vscode安装及环境配置教程(C/C)(Windows系统)_vscode搭建编译器环境-CSDN博客 vscode生成tasks.json、launch.json、c_cpp_properties.json文件_vscode生成launch.json-CSDN博客 自动生成配置文件简单方便&#xff01;&#xff01;&#xff01; 运行c代…

点击clean失败的操作Error running ‘demo4 [clean]‘ No valid Maven installation found.

错误情况&#xff1a; 解决方法&#xff1a; 重新调整自己的maven&#xff1a; 点击settings&#xff0c;搜索maven&#xff0c;点击进入&#xff1a; 选择自己的真实路径&#xff1a; 效果: 可以发现&#xff0c;运行clean成功&#xff01;