服务器销售网站源码,北京网站改版,跑腿app开发,网站开发怎么谈客户SpringMVC的消息转换器#xff08;Message Converter#xff09;是Spring框架中用于处理HTTP请求和响应体与Java对象之间转换的组件。它们使得开发人员可以轻松地将HTTP请求的数据映射到方法参数#xff0c;并将返回的对象转换为HTTP响应。
工作原理 当一个HTTP请求到达Spr… 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用于处理资源文件如文件下载
配置消息转换器 你可以通过以下几种方式配置消息转换器 通过Java配置类 如果你正在使用基于Java的配置可以通过实现WebMvcConfigurer接口并重写configureMessageConverters方法来添加自定义的消息转换器 Configuration
public class WebConfig implements WebMvcConfigurer {Overridepublic void configureMessageConverters(ListHttpMessageConverter? converters) {// 添加默认的消息转换器WebMvcConfigurer.super.configureMessageConverters(converters);// 添加自定义的消息转换器converters.add(new CustomHttpMessageConverter());}
} 通过Spring XML配置 如果你还在使用XML配置则可以在配置文件中定义mvc:message-converters元素 mvc:annotation-drivenmvc:message-converters register-defaultstruebean classorg.springframework.http.converter.json.MappingJackson2HttpMessageConverter/!-- 其他自定义的消息转换器 --/mvc:message-converters
/mvc:annotation-driven 自动配置Spring Boot 在Spring Boot中框架会根据类路径上的库自动配置合适的消息转换器。例如如果Jackson库在类路径上Spring Boot会自动注册MappingJackson2HttpMessageConverter。
自定义消息转换器 为了创建自定义的消息转换器你需要实现HttpMessageConverterT接口其中T是你想要转换的对象类型。这个接口有三个主要的方法需要实现
boolean canRead(Class? clazz, MediaType mediaType)判断是否能够读取指定类型的对象。boolean canWrite(Class? clazz, MediaType mediaType)判断是否能够写入指定类型的对象。ListMediaType getSupportedMediaTypes()返回支持的媒体类型列表。T read(Class? extends T clazz, HttpInputMessage inputMessage)从HTTP输入消息中读取并转换为对象。void write(T t, MediaType contentType, HttpOutputMessage outputMessage)将对象转换为HTTP输出消息。 一旦实现了上述接口就可以将其添加到SpringMVC的配置中以便框架知道在处理特定类型的HTTP消息时使用哪个转换器。除了实现HttpMessageConverterT接口外你还可以通过配置类或XML文件控制哪些转换器被使用以及它们的优先级顺序。这对于确保正确处理不同类型的消息非常重要。
自定义消息转换器实例
1. 创建业务对象 首先我们需要一个Java类来表示我们要序列化和反序列化的数据模型。这里我们创建一个简单的CustomObject
public class CustomObject {private Long id;private String name;// Getters and Setterspublic 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;}Overridepublic String toString() {return CustomObject{ id id , name name \ };}
}
2. 实现自定义消息转换器 接下来我们将创建一个自定义的消息转换器用于处理特定媒体类型的数据。假设我们的自定义媒体类型是application/vnd.example.v1json。
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 AbstractHttpMessageConverterCustomObject {private final ObjectMapper objectMapper new ObjectMapper();public CustomJsonHttpMessageConverter() {// 支持的媒体类型super(new MediaType(application, vnd.example.v1json));}Overrideprotected boolean supports(Class? clazz) {// 指定转换器支持的类型return CustomObject.class.isAssignableFrom(clazz);}Overrideprotected CustomObject readInternal(Class? extends CustomObject clazz, HttpInputMessage inputMessage)throws IOException, HttpMessageNotReadableException {// 读取并反序列化为CustomObjectreturn objectMapper.readValue(inputMessage.getBody(), clazz);}Overrideprotected 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 {Overridepublic void configureMessageConverters(ListHttpMessageConverter? 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.v1json, produces application/vnd.example.v1json)public CustomObject createCustomObject(RequestBody CustomObject customObject) {// 处理业务逻辑...System.out.println(Received: customObject);return customObject; // 返回相同的对象作为响应}GetMapping(value /{id}, produces application/vnd.example.v1json)public CustomObject getCustomObject(PathVariable Long id) {// 模拟查询操作CustomObject obj new CustomObject();obj.setId(id);obj.setName(Example Object id);return obj;}
}
5. 异常处理 当消息转换过程中发生错误时Spring MVC会抛出HttpMessageNotReadableException或HttpMessageNotWritableException。你可以通过全局异常处理器来捕获这些异常并返回友好的错误信息给客户端。
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 ResponseEntityString handleHttpMessageNotReadable(HttpMessageNotReadableException ex) {return new ResponseEntity(Error reading message: ex.getMessage(), HttpStatus.BAD_REQUEST);}ExceptionHandler(HttpMessageNotWritableException.class)public ResponseEntityString 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 {Beanpublic 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 AbstractHttpMessageConverterCustomObject {private final ObjectMapper objectMapper;Autowiredpublic CustomJsonHttpMessageConverter(ObjectMapper objectMapper) {super(new MediaType(application, vnd.example.v1json));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 {BeanConfigurationProperties(prefix spring.jackson)public ObjectMapper objectMapper() {return new ObjectMapper();}
}
在application.properties或application.yml中添加相应的配置项
spring.jackson.date-formatyyyy-MM-dd HH:mm:ss
spring.jackson.time-zoneUTC
# 更多配置...
自定义序列化器和反序列化器 有时默认的行为可能不符合业务需求这时可以通过实现自定义的序列化器和反序列化器来调整数据处理方式。以日期格式为例如果想要统一处理Java 8的时间类型可以注册JavaTimeModule模块
Bean
public Module javaTimeModule() {return new JavaTimeModule();
}
还可以通过继承JsonSerializer和JsonDeserializer来创建更加定制化的逻辑。
8. 性能优化 缓存ObjectMapper 确保ObjectMapper实例是单例且线程安全的因为它不是线程安全的。通常情况下Spring会为你管理这一点但如果你手动创建了ObjectMapper则需要特别注意。 异步处理 为了提高响应速度尤其是在处理大文件或复杂对象时可以考虑使用异步的消息转换。Spring MVC支持异步请求处理允许你在后台线程池中执行耗时任务而不阻塞主线程。
GetMapping(/async/{id})
public CompletableFutureCustomObject 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: falsedeserialization:fail-on-unknown-properties: false
11. 日志记录 启用详细的日志记录有助于调试和监控消息转换过程中的任何问题。可以在application.properties中配置日志级别
logging.level.org.springframework.webDEBUG
logging.level.com.fasterxml.jacksonDEBUG
12. 测试 编写单元测试和集成测试来验证消息转换器的行为是否符合预期非常重要。JUnit和MockMvc可以帮助你模拟HTTP请求并检查转换结果。
WebMvcTest
class CustomControllerTest {Autowiredprivate MockMvc mockMvc;Testvoid shouldReturnDefaultMessage() throws Exception {mockMvc.perform(get(/custom/1).accept(MediaType.parseMediaType(application/vnd.example.v1json))).andExpect(status().isOk()).andExpect(content().contentType(application/vnd.example.v1json)).andExpect(jsonPath($.name).value(Example Object 1));}
}
使用注解控制消息转换 在控制器层你可以使用一些注解来控制消息转换的行为比如RequestBody和ResponseBody
RequestBody用于将HTTP请求体的内容映射到方法参数并使用适当的HttpMessageConverter进行反序列化。ResponseBody用于指示方法的返回值应该被直接写入HTTP响应体而不是解析为视图。
此外还有RestController注解它是一个组合注解等价于同时使用Controller和ResponseBody简化了RESTful服务的开发。