《学会 SpringMVC 系列 · 返回值处理器》

news2024/9/25 3:29:05

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

文章目录

    • 写在前面的话
      • 学前准备与回顾
    • SpringMVC 返回值处理器
      • 技术说明
      • 接口信息
      • 常见列表
      • 应用场景
      • 自定义示例
      • RequestResponseBodyMethodProcessor
    • 总结陈词

CSDN.gif

写在前面的话

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

学前准备与回顾

本篇为 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

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


SpringMVC 返回值处理器

技术说明

HandlerMethodReturnValueHandler 是 Spring MVC 中的一个接口,用于处理控制器方法的返回值。它是处理返回值与 HTTP 响应之间关系的核心机制。通过自定义 HandlerMethodReturnValueHandler,可以实现特定返回值类型的处理逻辑,例如格式化输出、包装响应数据等。

接口信息

HandlerMethodReturnValueHandler 接口定义了两个主要方法:
boolean supportsReturnType(MethodParameter returnType):支持处理给定返回值类型的处理器方法。
void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest):处理实际的返回值。
MethodParameter:封装方法参数或方法返回值的元数据,包括类型、注解等信息。
ModelAndViewContainer:用于存储处理器方法的返回值以及视图名称或 View 对象。
NativeWebRequest:封装当前 HTTP 请求和响应。

常见列表

RequestResponseBodyMethodProcessor – 处理 @ResponseBody
ViewNameMethodReturnValueHandLer – 处理返回 String(视图)
自定义 HandlerMethodReturnValueHandler – 包装返回值输出

应用场景

统一响应格式:可以用于统一返回格式,将所有返回值封装为特定的响应对象。
数据包装:在返回数据之前,进行某些特定的包装操作,如添加元数据、状态码等。
权限过滤:在返回之前,检查并过滤掉用户无权限访问的数据。
日志记录:对返回的数据进行记录,用于审计或分析。

自定义示例

代码:study-up#MyHandlerMethodReturnValueHandler
逻辑:对添加了自定义注解的接口,包装上一层实体返回。
实现步骤:
Step1、添加自定义返回值处理器

public class MyHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler {

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResultModelAnnotation.class) || returnType.hasMethodAnnotation(ResultModelAnnotation.class));
    }

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
        ResultModel<Object> resultModel;

        String message = "";
        Operation methodAnnotation = returnType.getMethodAnnotation(Operation.class);
        if (methodAnnotation != null) {
            message = methodAnnotation.summary() + "成功";
        }

        if (returnValue instanceof ResultModel) {
            resultModel = (ResultModel<Object>) returnValue;
            if (!resultModel.isSuccess()) {
                resultModel.setMessage(message + "失败");
            }
        } else {
            resultModel = ResultModel.success(returnValue, message);
        }

        mavContainer.setRequestHandled(true);
        HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
        if (response != null) {
            response.setStatus(HttpStatus.OK.value());
            response.setHeader("result-model", "true");
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setCharacterEncoding("UTF-8");
            try (PrintWriter writer = response.getWriter()) {
                writer.write(JSON.toJSONString(resultModel, SerializerFeature.WriteMapNullValue));
                writer.flush();

                // 设置请求处理已完成,防止Spring继续处理返回值
                mavContainer.setRequestHandled(true);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
}

Step2、配置返回值处理器

Tips:使用addReturnValueHandlers方式要注意顺序的影响。

通过自定义 RequestMappingHandlerAdapter 的方式实现效果。

public class CustomResultMappingHandlerAdapter extends RequestMappingHandlerAdapter {

    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
        List<HandlerMethodReturnValueHandler> returnValueHandlers = super.getReturnValueHandlers();
        MyHandlerMethodReturnValueHandler handler = new MyHandlerMethodReturnValueHandler();
        List<HandlerMethodReturnValueHandler> list = new ArrayList<>();
        list.add(handler);
        list.addAll(returnValueHandlers);
        super.setReturnValueHandlers(list);
    }
}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Bean
    public WebMvcRegistrations feignWebRegistrations() {
        return new WebMvcRegistrations() {
            @Override
            public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
                return new CustomResultMappingHandlerAdapter();
            }
        };
    }

}

Step3、测试效果
正常书写Controller,该处理器会包裹ResultModel返回。

@RestController
@ResultModelAnnotation
@Tag(name = "教师信息控制层", description = "教师信息")
@RequestMapping(value = "/zyTeacherInfo")
public class ZyTeacherInfoController extends BaseController {

    @Autowired
    private ZyTeacherInfoService zyTeacherInfoService;

    @GetMapping("/{id}")
    public ZyTeacherInfo get(@PathVariable String id) {
        return zyTeacherInfoService.getById(id);
    }
}

返回结果如下:

{
  "code": "00000",
  "data": {
    "createdTime": "2024-05-16 20:07:21",
    "modifiedTime": "2024-07-29 14",
    "sortNo": null,
    "stuItem": null,
    "teaCode": "1",
    "teaConfig": null,
    "teaImg": null,
    "teaName": "张老师",
    "teaPhone": null,
    "teaType": null,
    "validFlag": "1"
  },
  "error": "",
  "message": "获取教师信息表详细信息成功",
  "success": true
}

观察源码可以看到,这里走的是自定义的返回值处理器,细节就不展开了。
image.png

RequestResponseBodyMethodProcessor

前面源码分析章节,可以看到,参数解析和返回值解析中,都用到了 RequestResponseBodyMethodProcessor,那它为何可以如此呢?其实看一下类图就明白了。
既实现了HandlerMethodReturnValueHandler接口,也实现了HandlerMethodArgumentResolver,看它的匹配方法也可以看出来,就是针对 @RequestBody 和 @ResponseBody 的处理器。
image.png


总结陈词

💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

CSDN_END.gif

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

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

相关文章

微调(二)

Selective类方法中的BitFit 它的核心思想是仅更新模型中的偏置项&#xff08;bias terms&#xff09;或部分偏置项&#xff0c;从而实现参数的稀疏更新。这种方法在小到中等规模的训练数据上表现出色&#xff0c;有时甚至能够超越全模型微调的性能。对于BERT模型&#xff0c;B…

【PGCCC】PostgreSQL 14 小版本分析,有那个版本不建议使用#PG中级

以下是对 PostgreSQL 14 各个小版本的详细分析&#xff0c;包括每个版本的主要变化、修复的 bug 和潜在的问题&#xff1a; PostgreSQL 14.0 发布日期&#xff1a;2021 年 9 月 30 日 主要变化&#xff1a; 增加了并行查询的改进&#xff0c;提升了性能。增强了 JSON 数据类…

美团2024年春招第一场笔试[测开方向],编程题+选择题详解,ACM式C++解法

编程题&选择题 编程题小美的平衡矩阵思路代码 小美的数组询问思路代码 验证工号思路代码 选择题1.在计算机网络中&#xff0c;端口号的作用是什么2.HTTPS协议通过使用哪些机制来确保通信的安全性3.Etag用于标识资源的唯一标识符&#xff0c;他可以用于4.在一个单道系统中&a…

乱弹篇(40)人类追求长寿

不要认为只有中国的老龄化才严重&#xff0c;实际上全球都面临老龄化&#xff0c;其中日本最为严重。 这是随着人类生活和医学水平的不断提高&#xff0c;寿命才会比过去数十年有了大幅度的提升。据资料显示&#xff0c;目前全球平均预期寿命估计为73岁。与百年之前相比&#…

DNS在架构中的使用

1 介绍 DNS&#xff08;Domain Name System&#xff0c;域名系统&#xff09;是一种服务&#xff0c;它是域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便的访问互联网&#xff0c;而不用去记住能够被机器直接读取的IP地址数串。简单来说&#xff0c;DNS就是…

dpdk实现udp协议栈

使用DPDK实现UDP用户态协议栈&#xff0c;实现流程中包括&#xff1a; 三类线程 1、收发包线程 2、用户态协议栈线程 3、udp服务端线程 两类缓冲区&#xff1a; 1、协议栈收包缓冲区和协议栈发包缓冲区 2、udp收包缓冲区和udp发包缓冲区 协议栈缓冲区中存储的数据是str…

在线考试系统产品分析与技术实现的深度融合

在当今数字化教育浪潮中&#xff0c;在线考试系统作为教育信息化的重要组成部分&#xff0c;正以前所未有的速度改变着传统教育模式。它不仅打破了地域和时间的限制&#xff0c;提高了考试效率与公平性&#xff0c;还通过数据分析为教育决策提供了科学依据。本文旨在探讨在线考…

集装箱排柜系统介绍

1.功能介绍 用户导入产品基本信息表&#xff0c;每个货号代表一种货物&#xff0c;它放一个立方体中&#xff0c;此立方体称为托。 之后&#xff0c;用户导入订单表&#xff0c;其中的货号是顾客订购的货物。 用户选好目的港、集装箱类型等信息&#xff0c;集装箱排柜系统开始计…

解锁AI潜能,引领智能新时代——《深度强化学习》

在人工智能的浪潮中&#xff0c;深度强化学习如同一股不可忽视的强流&#xff0c;正以前所未有的速度推动着科技的边界&#xff0c;引领我们进入一个充满无限可能的新时代。这本《深度强化学习》不仅是一部技术宝典&#xff0c;更是一场关于智能探索与梦想实现的深度对话&#…

太阳能光伏气象站:绿色能源与气象科技

在追求可持续发展的道路上&#xff0c;太阳能光伏气象站以其独特的创新设计&#xff0c;成为了绿色能源与气象科技融合的典范。这款设备不仅利用太阳能作为清洁能源供电&#xff0c;还集成了先进的气象监测技术&#xff0c;为光伏发电提供了精准的环境数据支持。 太阳能光伏气象…

无缝融入,即刻智能[1]:MaxKB知识库问答系统,零编码嵌入第三方业务系统,定制专属智能方案,用户满意度飙升

无缝融入,即刻智能[1]:MaxKB知识库问答系统,零编码嵌入第三方业务系统,定制专属智能方案,用户满意度飙升 1.简介 MaxKB(Max Knowledge Base)是一款基于 LLM 大语言模型的开源知识库问答系统, 官方网址:https://maxkb.cn/ GitHub:https://github.com/1Panel-dev/MaxKB…

pycharm如何查看git历史版本变更信息

通过名字查看不同版本 查看版本不同地方

Django REST Framework(十五)路由Routes

如何在Django REST framework中利用SimpleRouter和DefaultRouter来高效生成视图集的路由信息,并详细解释如何使用action装饰器为视图集中的自定义方法生成路由 1.路由的定义规则 路由称为URL(Uniform Resource Locator,统一资源定位符),也可以称为URLconf,是对可以从互联…

【xss-labs-master】靶场通关详解!-----持续更新

XSS基础概念&#xff1a; 跨站脚本攻击XSS(Cross Site Scripting)&#xff0c;为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆&#xff0c;故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码&#xff0c;当用户浏览该页之时&#xff0c;嵌入其…

在线考试系统产品源码功能架构与技术解析

首先&#xff0c;它极大地提升了考试的便捷性和效率&#xff0c;使得教育机构、企业乃至个人能够随时随地组织考试&#xff0c;打破了传统考试在时间和空间上的限制。其次&#xff0c;通过自动化的评分和数据分析功能&#xff0c;在线考试系统能够迅速反馈考试结果&#xff0c;…

vim列编辑模式

在编辑文本时&#xff0c;经常会有这样的需求&#xff0c;对特定列进行进行批量编辑。比如批量注释一段代码&#xff0c;或者删除待定字符&#xff08;如一列空格&#xff09;。幸运的是VIM支持列编辑模式。 假设文本内容&#xff1a; Maximum length of a custom vocabulary…

【Vulnhub系列】Vulnhub Connect-The-Dots 靶场渗透(原创)

【Vulnhub系列靶场】Vulnhub Connect-The-Dots靶场渗透 原文转载已经过授权 原文链接&#xff1a;Lusen的小窝 - 学无止尽&#xff0c;不进则退 (lusensec.github.io) 一、主机发现 二、端口扫描 PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 2.0.8 or…

剪画小程序:巴黎奥运会,从画面到声音!

在巴黎奥运会的赛场上&#xff0c;每一个瞬间都伴随着独特的声音。那是观众的欢呼&#xff0c;是运动员冲刺的呐喊&#xff0c;是国歌奏响的激昂旋律。 如今&#xff0c;通过剪画音频提取&#xff0c;我们能够将这些珍贵的声音从精彩的画面中分离出来&#xff0c;单独珍藏。 想…

2024.8.2 作业

1.互斥锁 #include <myhead.h>// 1、创建一个互斥锁 pthread_mutex_t mutex;int num 520; // 票的个数// 定义线程体1 void *task1(void *arg) {while (1){// 3、获取锁资源pthread_mutex_lock(&mutex);if (num > 0){usleep(1000);num--;printf("张三买了一…

GCKontrol-GCAir工具链在飞机功能系统设计中的应用

前言 当前&#xff0c;数字化转型正引领着飞行器研发方式向智能化、协同化、定制化、自主化等方向发展&#xff0c;为飞行器研发带来了新的机遇和挑战。其中&#xff0c;系统仿真作为数字化转型的重要工具&#xff0c;在飞行器研发过程中发挥着关键作用。国际上&#xff0c;各…