马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
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 {
- @Override
- public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
- // 添加默认的消息转换器
- WebMvcConfigurer.super.configureMessageConverters(converters);
- // 添加自定义的消息转换器
- converters.add(new CustomHttpMessageConverter());
- }
- }
复制代码 - 通过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>
复制代码 - 主动设置(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会抛出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 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.properties或application.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();
- }
复制代码 还可以通过继承JsonSerializer和JsonDeserializer来创建更加定制化的逻辑。
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服务的开辟。
|