SpringMVC的消息转换器

       SpringMVC的消息转换器(Message Converter)是Spring框架中用于处理HTTP请求和响应体与Java对象之间转换的组件。它们使得开发人员可以轻松地将HTTP请求的数据映射到方法参数,并将返回的对象转换为HTTP响应。

工作原理

       当一个HTTP请求到达Spring MVC应用程序时,框架会根据请求的内容类型(Content-Type)和接受类型(Accept)来选择合适的消息转换器。例如,如果客户端发送了一个JSON格式的POST请求,那么Spring MVC会选择MappingJackson2HttpMessageConverter来将请求体反序列化为Java对象。同样地,当方法返回一个Java对象并需要将其发送给客户端时,Spring MVC会使用相应的消息转换器来序列化这个对象。

常见的内置转换器

       在SpringMVC中,消息转换器通常通过HttpMessageConverter接口实现。Spring MVC提供了多种内置的消息转换器来支持不同的媒体类型,例如JSON、XML等。以下是几个常见的内置转换器:

  • MappingJackson2HttpMessageConverter:用于支持JSON格式的HTTP消息,依赖于Jackson库。
  • MappingJackson2XmlHttpMessageConverter:用于支持XML格式的HTTP消息,同样依赖于Jackson库。
  • StringHttpMessageConverter:用于处理纯文本字符串的HTTP消息。
  • FormHttpMessageConverter:用于处理表单数据(application/x-www-form-urlencoded或multipart/form-data),包括标准表单和文件上传。
  • ByteArrayHttpMessageConverter:用于处理二进制数据,比如图片或文件下载。
  • Jaxb2RootElementHttpMessageConverter:基于JAXB API,用于XML数据的序列化和反序列化。
  • SourceHttpMessageConverter:用于处理基于javax.xml.transform.Source的XML消息。
  • ResourceHttpMessageConverter:用于处理资源文件,如文件下载

配置消息转换器

     你可以通过以下几种方式配置消息转换器:

  1. 通过Java配置类

     

    如果你正在使用基于Java的配置,可以通过实现WebMvcConfigurer接口并重写configureMessageConverters方法来添加自定义的消息转换器:

    @Configuration
    public class WebConfig implements WebMvcConfigurer {
    
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            // 添加默认的消息转换器
            WebMvcConfigurer.super.configureMessageConverters(converters);
            // 添加自定义的消息转换器
            converters.add(new CustomHttpMessageConverter());
        }
    }
  2. 通过Spring XML配置

     

    如果你还在使用XML配置,则可以在配置文件中定义<mvc:message-converters>元素:

    <mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
            <!-- 其他自定义的消息转换器 -->
        </mvc:message-converters>
    </mvc:annotation-driven>
  3. 自动配置(Spring Boot)

     

    在Spring Boot中,框架会根据类路径上的库自动配置合适的消息转换器。例如,如果Jackson库在类路径上,Spring Boot会自动注册MappingJackson2HttpMessageConverter

自定义消息转换器

       为了创建自定义的消息转换器,你需要实现HttpMessageConverter<T>接口,其中T是你想要转换的对象类型。这个接口有三个主要的方法需要实现:

  • boolean canRead(Class<?> clazz, MediaType mediaType):判断是否能够读取指定类型的对象。
  • boolean canWrite(Class<?> clazz, MediaType mediaType):判断是否能够写入指定类型的对象。
  • List<MediaType> getSupportedMediaTypes():返回支持的媒体类型列表。
  • T read(Class<? extends T> clazz, HttpInputMessage inputMessage):从HTTP输入消息中读取并转换为对象。
  • void write(T t, MediaType contentType, HttpOutputMessage outputMessage):将对象转换为HTTP输出消息。

        一旦实现了上述接口,就可以将其添加到SpringMVC的配置中,以便框架知道在处理特定类型的HTTP消息时使用哪个转换器。除了实现HttpMessageConverter<T>接口外,你还可以通过配置类或XML文件控制哪些转换器被使用,以及它们的优先级顺序。这对于确保正确处理不同类型的消息非常重要。

自定义消息转换器实例

1. 创建业务对象

    首先,我们需要一个Java类来表示我们要序列化和反序列化的数据模型。这里我们创建一个简单的CustomObject

public class CustomObject {
    private Long id;
    private String name;

    // Getters and Setters
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "CustomObject{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

2. 实现自定义消息转换器

     接下来,我们将创建一个自定义的消息转换器,用于处理特定媒体类型的数据。假设我们的自定义媒体类型是application/vnd.example.v1+json

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

import java.io.IOException;
import java.util.Collections;

public class CustomJsonHttpMessageConverter extends AbstractHttpMessageConverter<CustomObject> {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public CustomJsonHttpMessageConverter() {
        // 支持的媒体类型
        super(new MediaType("application", "vnd.example.v1+json"));
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        // 指定转换器支持的类型
        return CustomObject.class.isAssignableFrom(clazz);
    }

    @Override
    protected CustomObject readInternal(Class<? extends CustomObject> clazz, HttpInputMessage inputMessage)
            throws IOException, HttpMessageNotReadableException {
        // 读取并反序列化为CustomObject
        return objectMapper.readValue(inputMessage.getBody(), clazz);
    }

    @Override
    protected void writeInternal(CustomObject object, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException {
        // 序列化CustomObject到输出流中
        objectMapper.writeValue(outputMessage.getBody(), object);
    }
}

3. 配置消息转换器

     为了使Spring MVC知道使用我们自定义的消息转换器,我们需要在配置类中注册它。下面是如何在基于Java的配置中做到这一点:

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 configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        // 添加自定义的消息转换器
        converters.add(new CustomJsonHttpMessageConverter());
        // 如果需要保留默认的消息转换器,可以这样添加
        // WebMvcConfigurer.super.configureMessageConverters(converters);
    }
}

4. 控制器层

     现在,我们可以创建一个控制器来使用这个自定义的消息转换器:

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/custom")
public class CustomController {

    @PostMapping(consumes = "application/vnd.example.v1+json", produces = "application/vnd.example.v1+json")
    public CustomObject createCustomObject(@RequestBody CustomObject customObject) {
        // 处理业务逻辑...
        System.out.println("Received: " + customObject);
        return customObject; // 返回相同的对象作为响应
    }

    @GetMapping(value = "/{id}", produces = "application/vnd.example.v1+json")
    public CustomObject getCustomObject(@PathVariable Long id) {
        // 模拟查询操作
        CustomObject obj = new CustomObject();
        obj.setId(id);
        obj.setName("Example Object " + id);
        return obj;
    }
}

5. 异常处理

     当消息转换过程中发生错误时,Spring MVC会抛出HttpMessageNotReadableExceptionHttpMessageNotWritableException。你可以通过全局异常处理器来捕获这些异常并返回友好的错误信息给客户端。

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<String> handleHttpMessageNotReadable(HttpMessageNotReadableException ex) {
        return new ResponseEntity<>("Error reading message: " + ex.getMessage(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(HttpMessageNotWritableException.class)
    public ResponseEntity<String> handleHttpMessageNotWritable(HttpMessageNotWritableException ex) {
        return new ResponseEntity<>("Error writing message: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

6. 全局配置与性能优化

     如果你有多个自定义转换器或者想要对所有转换器进行一些全局配置(比如设置日期格式),你可以在配置类中做进一步的调整。此外,对于高流量的应用程序,考虑使用缓存或异步处理以提高性能。

例如,你可以配置Jackson的ObjectMapper以更好地控制JSON的序列化和反序列化行为:

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule()); // 支持Java 8时间API
        // 这里还可以设置其他全局配置选项
        return mapper;
    }
}

然后,在你的自定义消息转换器中注入这个ObjectMapper bean:

import org.springframework.beans.factory.annotation.Autowired;

public class CustomJsonHttpMessageConverter extends AbstractHttpMessageConverter<CustomObject> {

    private final ObjectMapper objectMapper;

    @Autowired
    public CustomJsonHttpMessageConverter(ObjectMapper objectMapper) {
        super(new MediaType("application", "vnd.example.v1+json"));
        this.objectMapper = objectMapper;
    }

    // ... 省略其他方法 ...
}

    这将确保所有的CustomJsonHttpMessageConverter实例都使用同一个配置过的ObjectMapper,从而简化了配置并且提高了代码的一致性。

7. 配置与自定义

     使用@ConfigurationProperties进行配置

     对于复杂的配置需求,可以使用@ConfigurationProperties来创建一个配置类,从而将配置属性外部化。例如,如果你想要为Jackson ObjectMapper配置多个属性,你可以这样做:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JacksonConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.jackson")
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
}

application.propertiesapplication.yml中添加相应的配置项:

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=UTC
# 更多配置...
自定义序列化器和反序列化器

      有时默认的行为可能不符合业务需求,这时可以通过实现自定义的序列化器和反序列化器来调整数据处理方式。以日期格式为例,如果想要统一处理Java 8的时间类型,可以注册JavaTimeModule模块:

@Bean
public Module javaTimeModule() {
    return new JavaTimeModule();
}

还可以通过继承JsonSerializerJsonDeserializer来创建更加定制化的逻辑。

8. 性能优化

     缓存ObjectMapper

     确保ObjectMapper实例是单例且线程安全的,因为它不是线程安全的。通常情况下,Spring会为你管理这一点,但如果你手动创建了ObjectMapper,则需要特别注意。

     异步处理

     为了提高响应速度,尤其是在处理大文件或复杂对象时,可以考虑使用异步的消息转换。Spring MVC支持异步请求处理,允许你在后台线程池中执行耗时任务而不阻塞主线程。

@GetMapping("/async/{id}")
public CompletableFuture<CustomObject> getCustomObjectAsync(@PathVariable Long id) {
    return CompletableFuture.supplyAsync(() -> {
        // 模拟耗时操作
        try { Thread.sleep(1000); } catch (InterruptedException e) {}
        CustomObject obj = new CustomObject();
        obj.setId(id);
        obj.setName("Async Example Object " + id);
        return obj;
    });
}

9. 安全性考虑

     内容协商(Content Negotiation)

      确保你的应用程序正确地实现了内容协商机制,以防止攻击者利用不期望的内容类型发起攻击。可以通过设置白名单来限制允许的内容类型,并确保所有转换器都只处理经过验证的媒体类型。

@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
    configurer.favorPathExtension(false)
              .favorParameter(true)
              .parameterName("mediaType")
              .ignoreAcceptHeader(true)
              .useRegisteredExtensionsOnly(true)
              .defaultContentType(MediaType.APPLICATION_JSON)
              .mediaType("json", MediaType.APPLICATION_JSON)
              .mediaType("xml", MediaType.APPLICATION_XML);
}
     数据验证

     始终对输入的数据进行验证,即使是在序列化和反序列化之后。这可以帮助防止诸如注入攻击等安全问题。

10. Spring Boot集成

       在Spring Boot中,许多配置已经被简化并自动化。你只需要确保所需的库(如Jackson)存在于类路径上,框架就会自动配置合适的消息转换器。此外,Spring Boot还提供了额外的功能,比如自动发现和注册转换器。

# application.yml
spring:
  jackson:
    serialization:
      write-dates-as-timestamps: false
    deserialization:
      fail-on-unknown-properties: false

11. 日志记录

       启用详细的日志记录有助于调试和监控消息转换过程中的任何问题。可以在application.properties中配置日志级别:

logging.level.org.springframework.web=DEBUG
logging.level.com.fasterxml.jackson=DEBUG

12. 测试

       编写单元测试和集成测试来验证消息转换器的行为是否符合预期非常重要。JUnit和MockMvc可以帮助你模拟HTTP请求并检查转换结果。

@WebMvcTest
class CustomControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void shouldReturnDefaultMessage() throws Exception {
        mockMvc.perform(get("/custom/1").accept(MediaType.parseMediaType("application/vnd.example.v1+json")))
              .andExpect(status().isOk())
              .andExpect(content().contentType("application/vnd.example.v1+json"))
              .andExpect(jsonPath("$.name").value("Example Object 1"));
    }
}

使用注解控制消息转换

      在控制器层,你可以使用一些注解来控制消息转换的行为,比如@RequestBody@ResponseBody

  • @RequestBody:用于将HTTP请求体的内容映射到方法参数,并使用适当的HttpMessageConverter进行反序列化。
  • @ResponseBody:用于指示方法的返回值应该被直接写入HTTP响应体,而不是解析为视图。

此外,还有@RestController注解,它是一个组合注解,等价于同时使用@Controller@ResponseBody,简化了RESTful服务的开发。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android 性能优化:内存优化(实践篇)

1. 前言 前一篇文章Android性能优化&#xff1a;内存优化 &#xff08;思路篇&#xff09; 大概梳理了Android 内存原理和优化的必要性及应该如何优化&#xff0c;输出了一套短期和长期内存优化治理的SOP方案。 那么这一篇文章就总结下我最近在做内存优化如何实践的&#xff0…

「Mac畅玩鸿蒙与硬件53」UI互动应用篇30 - 打卡提醒小应用

本篇教程将实现一个打卡提醒小应用&#xff0c;通过用户输入时间进行提醒设置&#xff0c;并展示实时提醒状态&#xff0c;实现提醒设置和取消等功能。 关键词 打卡提醒状态管理定时任务输入校验UI交互 一、功能说明 打卡提醒小应用包含以下功能&#xff1a; 提醒时间输入与…

Nginx知识详解(理论+实战更易懂)

目录 一、Nginx架构和安装 1.1 Nginx 概述 1.1.1 nginx介绍 1.1.2?Nginx 功能介绍 1.1.3?基础特性 1.1.4?Web 服务相关的功能 1.2?Nginx 架构和进程 1.2.1?Nginx 进程结构 1.2.2?Nginx 进程间通信 1.2.3?Nginx 启动和 HTTP 连接建立 1.2.4?HTTP 处理过程 1…

Postgresql 命令还原数据库

因为PgAdmin打不开&#xff0c;但是数据库已经安装成功了&#xff0c;这里借助Pg命令来还原数据库 C:\Program Files\PostgreSQL\15\bin\psql.exe #链接数据库 psql -U postgres -p 5432#创建数据库 CREATE DATABASE "数据库名称"WITHOWNER postgresENCODING UTF8…

Vue 解决浏览器刷新路由参数丢失问题 全局统一配置无需修改组件

在路由跳转的时候,我们经常会传一些参数过去,然后通过传过来的参数调用接口获取相关数据,但是刷新浏览器的时候路由参数会丢失。此时页面报错误了,如何通过全局配置的方式,不需要修改任何组件 实现刷新浏览器保存参数? 实现方式如下: 首先在router/index.js里添加参数管…

【AIGC】电话录音转文字实践:基于Google Cloud Speech-to-Text-v1的技术方案Python

文章目录 引言技术原理技术方案设计系统架构关键技术要点 代码实现1. 环境准备2. 核心代码实现3. 音频预处理工具响应格式 性能优化实践经验应用场景未来展望总结 引言 在当今数字化时代&#xff0c;将语音内容转换为文字已经成为一个非常重要的技术需求。无论是客服通话记录、…

RabbitMQ-基本使用

RabbitMQ: One broker to queue them all | RabbitMQ 官方 安装到Docker中 docker run \-e RABBITMQ_DEFAULT_USERrabbit \-e RABBITMQ_DEFAULT_PASSrabbit \-v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5672:5672 \--network mynet\-d \rabbitmq:3…

Android Camera压力测试工具

背景描述&#xff1a; 随着系统的复杂化和业务的积累&#xff0c;日常的功能性测试已不足以满足我们对Android Camera相机系统的测试需求。为了确保Android Camera系统在高负载和多任务情况下的稳定性和性能优化&#xff0c;需要对Android Camera应用进行全面的压测。 对于压…

vscode中调用deepseek实现AI辅助编程

来自 Python大数据分析 费弗里 1 简介 大家好我是费老师&#xff0c;最近国产大模型Deepseek v3新版本凭借其优秀的模型推理能力&#xff0c;讨论度非常之高&#x1f525;&#xff0c;且其官网提供的相关大模型API接口服务价格一直走的“价格屠夫”路线&#xff0c;性价比很高…

基于 LMS 算法的离散傅里叶分析器

基于 LMS&#xff08;Least Mean Squares&#xff0c;最小均方&#xff09;算法的离散傅里叶分析器是一种结合自适应滤波和频域分析的工具&#xff0c;用于动态估计信号的频谱成分。它将 LMS 自适应算法与离散傅里叶变换&#xff08;DFT&#xff09;的频率分解能力结合&#xf…

2022浙江大学信号与系统笔记

原视频地址&#xff1a;2022浙江大学信号与系统&#xff08;含配套课件和代码&#xff09; - 胡浩基老师-哔哩哔哩 ⭐⭐⭐ 我的笔记&#xff1a;飞书链接 - 信号与系统 基于视频&#xff0c;记得笔记&#xff0c;加了点自己的补充&#xff08;有的是问 ChatGPT 的&#xff09;…

K8s高可用集群之Kubernetes集群管理平台、命令补全工具、资源监控工具部署、常用命令

K8s高可用集群之Kubernetes管理平台、补全命令工具、资源监控工具部署 1.Kuboard可视化管理平台2.kubectl命令tab补全工具3.MetricsServer资源监控工具4.Kubernetes常用命令 1.Kuboard可视化管理平台 可以选择安装k8s官网的管理平台&#xff1b;我这里是安装的其他开源平台Kub…

Gitlab-runner 修改默认的builds_dir并使用custom_build_dir配置

gitlab-runner 修改默认的builds_dir并使用custom_build_dir配置 1. 说明2. 实操&#xff08;以docker执行器为例&#xff09;2.1 修改默认的builds_dir2.1.1 调整gitlab-runner的配置文件2.1.2 CI文件 2.2 启用custom_build_dir2.2.1 调整gitlab-runner的配置文件2.2.2 CI文件…

网络IP协议

IP&#xff08;Internet Protocol&#xff0c;网际协议&#xff09;是TCP/IP协议族中重要的协议&#xff0c;主要负责将数据包发送给目标主机。IP相当于OSI&#xff08;图1&#xff09;的第三层网络层。网络层的主要作用是失陷终端节点之间的通信。这种终端节点之间的通信也叫点…

SpringCloud源码-Ribbon

一、Spring定制化RestTemplate&#xff0c;预留出RestTemplate定制化扩展点 org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration 二、Ribbon定义RestTemplate Ribbon扩展点功能 org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguratio…

MySQL5.7.26-Linux-安装(2024.12)

文章目录 1.下载压缩包1.访问MySQL版本归档2.找到5.7.26并下载3.百度网盘 2.Linux安装1.卸载原来的MySQL8.0.26&#xff08;如果没有则无需在意&#xff09;1.查看所有mysql的包2.批量卸载3.删除残留文件**配置文件**&#xff08;默认路径&#xff09;&#xff1a; 4.**验证卸载…

python修改ppt中的文字部分及插入图片

批量修改ppt中的某个模块&#xff0c;或者批量制作奖状等场景会用到&#xff1b; import os import pandas as pd from pptx import Presentation from pptx.util import Inchesfilepath/Users/kangyongqing/Documents/kangyq/202303/分析模版/批量制作/file1时段预警_副本.pp…

Ubuntu24.04.1 LTS+Win11双系统安装记录

Win11相关 1.用DiskGenius删除硬盘分区 2.关闭win11的BitLocker&#xff0c;否则禁用安全启动后开机时需要帐户密钥&#xff0c;很麻烦。 3.在设备管理器中找到独立显卡&#xff0c;右键禁用。等ubuntu装好显卡驱动后&#xff0c;再进入win启用。 Ubuntu相关 1.Ubuntu24.04在…

covid-vaccine-availability-using-flask-server

使用烧瓶服务器获得 Covid 疫苗 原文:https://www . geesforgeks . org/co vid-疫苗-可用性-使用-烧瓶-服务器/ 在本文中&#xff0c;我们将使用 Flask Server 构建 Covid 疫苗可用性检查器。 我们都知道&#xff0c;整个世界都在遭受疫情病毒的折磨&#xff0c;唯一能帮助我们…

机器学习笔记 - 单幅图像深度估计的最新技术

1、深度估计简述 单眼深度估计是一项计算机视觉任务,AI 模型从单个图像中预测场景的深度信息。模型估计场景中对象从一个照相机视点的距离。单目深度估计已广泛用于自动驾驶、机器人等领域。深度估计被认为是最困难的计算机视觉任务之一,因为它要求模型理解对象及其深度信息之…