【Spring相关技术】spring进阶-自定义请求报文转对象HttpMessageConverter

news2025/1/19 7:06:50

文章目录

    • 类继承体系
    • 核心类与接口说明
    • 底层调用链
    • 完整示例
      • 步骤 1: 创建自定义的HttpMessageConverter
      • 步骤 2: 配置Spring MVC使用自定义转换器
      • 步骤 3: 使用自定义转换器
    • 相关文献

类继承体系

默认转换器即springmvc默认的转换器, 用的比较多的是以下两种, 无需自己声明以及使用。
相关的类的内容这里不做展示,有兴趣大家可以下载源码看一下。

  • 默认json转换器
    json-convertor

  • 默认xml转换器
    xml-convertor

核心类与接口说明

在Spring框架中,HttpMessageConverter 是一个非常重要的接口,它用于在HTTP请求和响应与Java对象之间进行转换。如果你需要实现自定义的报文转换器,可以按照以下步骤进行:

  1. 实现HttpMessageConverter接口:创建一个类实现HttpMessageConverter<T>接口,其中T是你希望转换的目标类型。你需要实现以下方法:

    • canRead(Class<?> clazz, MediaType mediaType):判断转换器是否能够读取指定类型和媒体类型的数据。
    • canWrite(Class<?> clazz, MediaType mediaType):判断转换器是否能够写入指定类型和媒体类型的数据。
    • getSupportedMediaTypes():返回转换器支持的媒体类型列表。
    • read(Class<? extends T> clazz, HttpInputMessage inputMessage):从HttpInputMessage中读取数据并转换为T类型的对象。
    • write(T t, MediaType contentType, HttpOutputMessage outputMessage):将T类型的对象转换并写入HttpOutputMessage中。
  2. 继承AbstractHttpMessageConverter:Spring提供了AbstractHttpMessageConverter类,它简化了HttpMessageConverter接口的实现。你只需要重写supports(Class<?> clazz)readInternal(Class<? extends T> clazz, HttpInputMessage inputMessage)writeInternal(T t, HttpOutputMessage outputMessage)方法。

  3. 配置Spring MVC使用自定义转换器:你需要在Spring的配置中注册自定义的HttpMessageConverter。这可以通过实现WebMvcConfigurer接口并重写configureMessageConvertersextendMessageConverters方法来完成。例如:

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add(new MyMessageConverter());
        }
    }
    

    其中MyMessageConverter是你的自定义转换器类。

  4. 使用自定义转换器:一旦注册,Spring MVC将自动使用你的自定义转换器来处理匹配的请求和响应。

在实现自定义转换器时,你可以定义自己的序列化和反序列化逻辑,例如处理XML、JSON或其他自定义格式的数据。自定义转换器可以让你更灵活地处理HTTP请求和响应数据,满足特定的业务需求。

底层调用链

在Spring MVC中,HttpMessageConverter 是一个核心接口,它负责将HTTP请求和响应的消息体与Java对象之间进行转换。下面我将解释Spring MVC中调用这些报文转换器的流程。

  1. 请求到达:当一个HTTP请求到达Spring MVC的DispatcherServlet时,它将请求委托给RequestMappingHandlerMapping来解析。

  2. 选择处理器RequestMappingHandlerMapping根据请求的URL和HTTP方法等信息找到匹配的控制器方法。

  3. 处理器适配器:找到控制器方法后,DispatcherServlet使用一个适当的HandlerAdapter来处理请求。对于使用@RequestMapping注解的方法,通常会使用RequestMappingHandlerAdapter

  4. 调用链

    • RequestMappingHandlerAdapter中的invokeHandlerMethod方法被调用。
    • 在这个方法内部,会创建一个ServletInvocableHandlerMethod对象,该对象封装了对控制器方法的调用。
    • ServletInvocableHandlerMethod对象持有从RequestMappingHandlerAdapter获取的messageConverters列表,这个列表包含了所有可用的消息转换器。
  5. 参数解析

    • 如果控制器方法有参数,并且这些参数需要从请求体中读取(例如,使用了@RequestBody注解),ServletInvocableHandlerMethod将使用RequestResponseBodyMethodProcessor来解析这些参数。
    • RequestResponseBodyMethodProcessor会遍历所有的messageConverters,使用canRead方法检查哪个转换器能够读取给定的媒体类型(Media Type)和目标类。
    • 一旦找到合适的转换器,它将使用read方法将请求体转换为相应的Java对象。
  6. 控制器方法执行:参数解析完成后,控制器方法被执行。

  7. 处理返回值

    • 控制器方法执行后,如果方法上有@ResponseBody注解,ServletInvocableHandlerMethod将再次使用RequestResponseBodyMethodProcessor来处理返回值。
    • RequestResponseBodyMethodProcessor会遍历所有的messageConverters,使用canWrite方法检查哪个转换器能够写入给定的媒体类型和对象类。
    • 找到合适的转换器后,它将使用write方法将Java对象转换为响应体消息。
  8. 响应返回:最终,转换后的消息体被写入HTTP响应中,并返回给客户端。

在整个调用链中,HttpMessageConverter的实现类负责具体的序列化和反序列化逻辑。开发者可以通过实现WebMvcConfigurer接口的extendMessageConverters方法来添加自定义的转换器,或者通过配置文件中的<mvc:message-converters>标签来注册自定义转换器。

这个调用流程涉及到了多个组件和步骤,每个组件都在Spring MVC的请求处理中扮演了特定的角色。通过这种方式,Spring MVC提供了高度可定制的消息转换机制,以适应不同的数据处理需求。

完整示例

下面我将通过一个简单的例子来说明如何创建并集成一个自定义的HttpMessageConverter到Spring MVC中。

假设我们有一个简单的Java对象MyObject,我们想要通过一种非标准的媒体类型application/x-myobject来序列化和反序列化这个对象。我们将创建一个自定义的HttpMessageConverter来处理这个任务。

步骤 1: 创建自定义的HttpMessageConverter

首先,我们创建一个类MyObjectHttpMessageConverter,继承自AbstractHttpMessageConverter<MyObject>

import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;

public class MyObjectHttpMessageConverter extends AbstractHttpMessageConverter<MyObject> {

    public MyObjectHttpMessageConverter() {
        super(new MediaType("application", "x-myobject"));
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return MyObject.class.isAssignableFrom(clazz);
    }

    @Override
    protected MyObject readInternal(Class<? extends MyObject> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        String body = StreamUtils.copyToString(inputMessage.getBody(), Charset.defaultCharset());
        // 假设MyObject的构造函数接受一个字符串
        return new MyObject(body);
    }

    @Override
    protected void writeInternal(MyObject myObject, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        OutputStreamWriter writer = new OutputStreamWriter(outputMessage.getBody(), getCharset());
        // 假设我们要将MyObject转换成字符串进行输出
        writer.write(myObject.toString());
        writer.flush();
    }
}

步骤 2: 配置Spring MVC使用自定义转换器

接下来,我们需要在Spring MVC的配置中注册我们的自定义转换器。如果你使用的是Java配置,可以像下面这样实现WebMvcConfigurer接口:

import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    	// 如何自定义的转换器与默认转换器冲突,可以将自己的转换器放到默认转换器前面
        converters.add(new MyObjectHttpMessageConverter());
    }
}

如果你使用的是XML配置,可以在<mvc:annotation-driven/>标签中添加一个<bean>来注册你的转换器:

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="com.example.MyObjectHttpMessageConverter"/>
    </mvc:message-converters>
</mvc:annotation-driven>

步骤 3: 使用自定义转换器

现在,当你的控制器方法返回一个MyObject类型的实例,或者接受一个MyObject类型的参数时,Spring MVC将使用你的自定义转换器来处理序列化和反序列化。

例如,你的控制器可能看起来像这样:

@RestController
public class MyObjectController {

    @PostMapping(value = "/myobject", consumes = "application/x-myobject")
    public MyObject handleMyObject(@RequestBody MyObject myObject) {
        // 处理myObject
        return myObject;
    }
}

在这个例子中,当客户端以application/x-myobject类型发送数据到/myobject端点时,Spring MVC将使用MyObjectHttpMessageConverter来解析请求体中的字符串并创建MyObject实例。同样,当控制器方法返回一个MyObject实例时,Spring MVC也将使用这个转换器将实例转换为响应体。

这样,你就成功地将自定义的报文转换器集成到了Spring MVC中。

相关文献

【java知识】java基础-xstream解析xml

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

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

相关文章

生信技能61 - 获取比对后BAM文件的多项基础统计指标

获取比对后BAM文件的多项基础统计指标 1. 运行实例 采用pysam库解析bam文件,将bam文件路径作为输入参数,统计输出比对的参考基因组大小、ATCGN各碱基数量/比率、GC含量、UR reads数量、平均测序深度、总reads数量等统计指标。 python bam_statistics.py -b sample.sorted.…

【Nestjs】从入门到精通(依赖注入)

NestJS 是一个基于 Node.js 的渐进式框架&#xff0c;构建在 Express 或 Fastify 之上&#xff0c;主要用于构建高效、可扩展的服务器端应用程序。它使用 TypeScript 并借鉴了 Angular 的设计理念&#xff0c;采用了依赖注入&#xff08;IoC, Inversion of Control&#xff09;…

动态网站及爬虫技术应用(题目)

/*T26:HTTP响应消息的状态代码为500时表示&#xff08; &#xff09;: HTTP响应消息的状态代码为500时表示服务器内部错误&#xff08;Internal Server Error&#xff09;。这通常意味着服务器在处理请求时遇到了意外的情况&#xff0c;导致无法完成该请求。这种错误可能是由于…

操作系统Lesson - 5 进程简介及进程编程模型

文章目录 什么是进程同一程序如何并发&#xff1f; Linux进程常用命令proc目录 PCB进程控制块PCB包含的信息两个进程间的关系 进程编程exec() 替换函数子进程的运行过程 什么是进程 进程 程序运行的过程&#xff0c;系统进行资源管理和调度的独立单位。 程序由两部分组成&am…

基础入门-Web应用架构搭建漏洞HTTP数据包

网站搭建 这里给一个网站搭建的视频链接&#xff0c;之前又搞过搭建服务器&#xff0c;这里就不再重做了 https://www.bilibili.com/video/BV16A4y1X7vg/?spm_id_from333.337.search-card.all.click&vd_sourcec2c3c616b3ad1faf44a9f6f30a4dbb03 值得注意的是&#xff0c;…

网络通信与并发编程(二)基于tcp的套接字、基于udp的套接字、粘包现象

基于tcp的套接字 文章目录 基于tcp的套接字一、套接字的工作流程二、基于tcp的套接字通信三、基于udp的套接字通信四、粘包现象 一、套接字的工作流程 Socket是应用层与TCP/IP协议族通信的中间软件抽象层&#xff0c;它是一组接口。在设计模式中&#xff0c;Socket其实就是一个…

学习底座架构-武汉

1 学习底座架构概述 大脑学习中心-边缘系统 一、当下教育现状 二、什么是学习底座 三、学习底座价值 七、学习底座解决问题的流程 案例&#xff1a;以4R注意力为例 一 注意力问题解决流程 二 注意力问题的危害 三 衡量注意力水平高低的标准 四 注意力问题4大根源 & 2大诱因…

FPGA驱动HDMI 初级篇

简介 本章节主要讲述如何通过FPGA驱动HDMI显示。 本章节框图如下: bd框图中使用了两个IP核,分别是Video Timing controller核AXI4-Stream to video out两个模块,下面先对两个模块做介绍。 Video Timing controller 配置如下: 这里由于没有使用ZYNQ PS端,…

BKD树介绍、区别与联系

简介 BKD树&#xff08;全称 bushy kd-trees&#xff09;是一种用于高维数据搜索的数据结构。它结合了K-D树和B树的特点&#xff0c;旨在提高多维空间数据的索引和查询效率。 基本概念 BKD树是一种二叉树结构&#xff0c;类似于K-D树&#xff0c;但其设计更加注重空间利用率…

10.11Python数学基础-多维随机变量及其分布

多维随机变量及其分布 1.二维随机变量及其分布 假设E是随机试验&#xff0c;Ω是样本空间&#xff0c;X、Y是Ω的两个变量&#xff1b;(X,Y)就叫做二维随机变量或二维随机向量。X、Y来自同一个样本空间。 联合分布函数 F ( x , y ) P ( X ≤ x , Y ≤ y ) F(x,y)P(X≤x,Y≤…

SpringBoot+XXL-JOB:高效定时任务管理

前言 在现代应用程序中&#xff0c;定时任务是不可或缺的一部分。Spring Boot 和 XXL-Job 为你提供了一个强大的工具组合&#xff0c;以简化任务调度和管理。 本文将带领你探索如何将这两者集成在一起&#xff0c;实现高效的定时任务管理。无论你是初学者还是有经验的开发者&…

【进阶】面向对象之接口

文章目录 为什么要有接口如何定义和使用一个接口练习 接口里面的成员特点接口和类之间的关系 为什么要有接口 接口:就是一种规则 如何定义和使用一个接口 接口用关键字interface来定义 public interface 接口名{}接口不能实例化接口和类之间是实现关系&#xff0c;通过imple…

集合的基础操作

1.集合中元素没有顺序&#xff0c;且不会重复&#xff0c;输入的数据若有重复则会去重 2.集合的格式为:变量名{##,##,##} 3.集合中元素的添加格式为&#xff1a;变量名.add(添加的元素) 4.集合中元素的取出格式&#xff08;取出元素后&#xff0c;原集合中该元素没有了&#…

软件测试学习笔记丨Linux三剑客-grep

本文转自测试人社区&#xff0c;原文链接&#xff1a;https://ceshiren.com/t/topic/32506 一、简介 1.1 grep命令 grep是一个全局查找正则表达式&#xff0c;并且打印结果行的命令。grep的输入是一个文件或者一个标准输入&#xff08;stdin&#xff09;&#xff0c;或者是一…

JAVA多线程实现

一、方法总结 Thread类的常用方法 void setName(String name)//将此线程的名称更改为name String getName()//如果不设置名称&#xff0c;则线程默认名称为Thread-0或Thread-1或…就是Thread -&#xff1f; String Thread.currentThread().getName()//获得当前线程名称 …

C++常见的内存错误和解决策略

目录 1.未初始化指针 (Uninitialized Pointer) 2.内存分配未成功却使用了它 3.野指针 (Dangling Pointer) 4.内存泄漏 (Memory Leak) 5.重复释放内存 (Double Free) 6.内存越界访问 (Buffer Overflow) 7.错误的数组删除方式 (Mismatched Delete) 8.栈内存溢出 (Stack O…

24/10/14 算法笔记 循环神经网络RNN

RNN: 一种专门用于处理序列数据的神经网络&#xff0c;它能够捕捉时间序列中的动态特征。RNN的核心特点是其循环连接&#xff0c;这允许网络在不同时间步之间传递信息&#xff0c;从而实现对序列数据的记忆和处理能力。 应用的场景&#xff1a; 自然语言处理&#xff08;NLP&…

这款懂人情世故的大模型强得可怕!

这款孵化于首期书生大模型实战营&#xff0c;基于 InternLM2 开发的项目——天机&#xff0c;更懂人情世故的大模型&#xff0c;这 2 天在社区可谓有点火&#xff01;相关内容在小红书上至少收获了六千多点赞与收藏 ! 你是否还在苦苦挣扎于各种应酬&#xff0c;四处寻找“高情商…

MySQL-04.DDL-数据库操作

一.数据库的操作 DDL(data definition language)&#xff1a;数据定义语言&#xff0c;用来定义数据库对象(数据库、表) DDL分为两类&#xff1a;1.数据库的DDL语句&#xff0c;主要是针对数据库的定义&#xff0c;增加&#xff0c;删除&#xff0c;使用 2.表结构的DDL语句&…

大数据-160 Apache Kylin 构建Cube 按照日期构建Cube 详细记录

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…