MappingJackson2HttpMessageConverter是SpringMVC默认的Json消息转化器。
1.应用场景
例如:一个@RestController类里有个test8()方法:
@RequestMapping(value = "/test8", method = RequestMethod.GET)
public ResponseEntity test8(@RequestParam(value = "account") String account){
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
SecUserMapper secUserMapperBatch=sqlSession.getMapper(SecUserMapper.class);
List accountList = new ArrayList();
accountList.add(account);
List result =null;
for(int i=0;i(result, HttpStatus.OK);
}
public class User {
private Long id;
private String account;
private Date modifyDate;
}
测试结果:
[{"id":1001125070,"account":"caifz","modifyDate":"2021-12-24T10:01:00.000+0000"}]
这里的modifyDate的格式不规范,我们希望请求结果返回“yyyy-MM-dd HH:mm:ss”格式的日期。在每个方法里写日期的转换?那就累死程序猿了。
统一设置返回结果的格式
新建一个config类,实现WebMvcConfigurer接口,继承这个接口后实现extendMessageConverters()方法,这个方法里定义
@Configuration
public class ControllerConfiguration implements WebMvcConfigurer,ApplicationContextAware {
private ApplicationContext applicationContext;
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper1=mappingJackson2HttpMessageConverter.getObjectMapper();
objectMapper1.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
return mappingJackson2HttpMessageConverter;
}
@Override
public void extendMessageConverters(List> converters) {
converters.add(mappingJackson2HttpMessageConverter());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
applicationContext.getBean(MappingJackson2HttpMessageConverter.class);
}
}
2.机制原理
HttpMessageConverter接口
使用debug模式查看converters里包含哪些HttpMessageConverter,发现在添加我们定义的mappingJackson2HttpmessageConverter前,已经有10个了。
HttpMessageConverter 接口介绍
这个接口的作用:Strategy interface that specifies a converter that can convert from and to HTTP requests and responses。策略接口定义了http请求转换为Java对象和Java对象转为http响应。接口有5个方法:canRead(类型是否可读),read(可读则读),canWrite(是否可写),write(可写则写),getSupportedMediaTypes(获取支持的媒体类型)。
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.http.converter;
import java.io.IOException;
import java.util.List;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
/**
* Strategy interface that specifies a converter that can convert from and to HTTP requests and responses.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
* @param the converted object type
*/
public interface HttpMessageConverter {
/**
* Indicates whether the given class can be read by this converter.
* @param clazz the class to test for readability
* @param mediaType the media type to read (can be {@code null} if not specified);
* typically the value of a {@code Content-Type} header.
* @return {@code true} if readable; {@code false} otherwise
*/
boolean canRead(Class> clazz, @Nullable MediaType mediaType);
/**
* Indicates whether the given class can be written by this converter.
* @param clazz the class to test for writability
* @param mediaType the media type to write (can be {@code null} if not specified);
* typically the value of an {@code Accept} header.
* @return {@code true} if writable; {@code false} otherwise
*/
boolean canWrite(Class> clazz, @Nullable MediaType mediaType);
/**
* Return the list of {@link MediaType} objects supported by this converter.
* @return the list of supported media types
*/
List getSupportedMediaTypes();
/**
* Read an object of the given type from the given input message, and returns it.
* @param clazz the type of object to return. This type must have previously been passed to the
* {@link #canRead canRead} method of this interface, which must have returned {@code true}.
* @param inputMessage the HTTP input message to read from
* @return the converted object
* @throws IOException in case of I/O errors
* @throws HttpMessageNotReadableException in case of conversion errors
*/
T read(Class extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
/**
* Write an given object to the given output message.
* @param t the object to write to the output message. The type of this object must have previously been
* passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}.
* @param contentType the content type to use when writing. May be {@code null} to indicate that the
* default content type of the converter must be used. If not {@code null}, this media type must have
* previously been passed to the {@link #canWrite canWrite} method of this interface, which must have
* returned {@code true}.
* @param outputMessage the message to write to
* @throws IOException in case of I/O errors
* @throws HttpMessageNotWritableException in case of conversion errors
*/
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException;
}
HttpMessageConverter接口和MappingJackson2HttpMessageConverter实现类
HttpMessageConverter接口和MappingJackson2HttpMessageConverter实现类的集成关系如下:
MappingJackson2HttpMessageConverter介绍
MappingJackson2HttpMessageConverter实现了HttpMessageConverter,使用Jackson的ObjectMapper实现了读Json和写Json。MappingJackson2HttpMessageConverter的主要逻辑在AbstractJackson2HttpMessageConverter类中。
ObjectMapper介绍
ObjectMapper提供了读写JSON的功能,往返于基本POJO(普通旧Java对象)或往返于通用JSON树模型,以及用于执行转换的相关功能。
HttpMessageConverter是如何配置的?
当我们没有配置HttpMessageConverter时,springmvc正常转换参数和返回结果。Spring boot肯定提供了默认配置。
spring-boot-autoconfigure包里提供了WebMvcAutoConfiguration,HttpMessageConvertersAutoConfiguration,增加了默认的10个HttpMessageConverter。
- 1.WebMvcAutoConfiguration里包含下面的配置使得WebMvcConfigurationSupport类成为配置类。(EnableWebMvcConfiguration 继承DelegatingWebMvcConfiguration,DelegatingWebMvcConfiguration继承WebMvcConfigurationSupport)
/**
* Configuration equivalent to {@code @EnableWebMvc}.
*/
@Configuration
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration
- 2.WebMvcConfigurationSupport创建Bean-->getMessageConverters()-->addDefaultHttpMessageConverters(),在addDefaultHttpMessageConverters()里新建了8个HttpMessageConverter。
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(mvcContentNegotiationManager());
adapter.setMessageConverters(getMessageConverters());
...
}
protected final List> getMessageConverters() {
if (this.messageConverters == null) {
this.messageConverters = new ArrayList<>();
configureMessageConverters(this.messageConverters);
if (this.messageConverters.isEmpty()) {
addDefaultHttpMessageConverters(this.messageConverters);
}
extendMessageConverters(this.messageConverters);
}
return this.messageConverters;
}
/**
* Adds a set of default HttpMessageConverter instances to the given list.
* Subclasses can call this method from {@link #configureMessageConverters}.
* @param messageConverters the list to add the default message converters to
*/
protected final void addDefaultHttpMessageConverters(List> messageConverters) {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringHttpMessageConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new ResourceRegionHttpMessageConverter());
...
}
- 3.HttpMessageConvertersAutoConfiguration注入了2个HttpMessageConverter:StringHttpMessageConverter和MappingJackson2HttpMessageConverter。
public HttpMessageConvertersAutoConfiguration(
ObjectProvider> convertersProvider) {
this.converters = convertersProvider.orderedStream().collect(Collectors.toList());
}
@Bean
@ConditionalOnMissingBean
public HttpMessageConverters messageConverters() {
return new HttpMessageConverters(this.converters);
}
- 4.自定义配置类里重载了extendMessageConverters(),添加了1个mappingJackson2HttpMessageConverter。
@Override
public void extendMessageConverters(List> converters) {
converters.add(mappingJackson2HttpMessageConverter());
System.out.println("");
}
3.数据流转
如何梳理一个请求的数据流转?
首先找到一个所有请求都会经过的类DispatchServlet,找到其中的doDispatch方法,打个断点,然后使用IDEA的debug模式,一路跟踪代码。
4.搭配使用ResponseBodyAdvice和ControllerAdvice
- @ControllerAdvice通过切面的方式给controller类加了一个增强器,assignableTypes 指定需要增强的Controller类。
- @ExceptionHandler这个注解则表示Controller中任何一个方法发生异常,则会被注解了@ExceptionHandler的方法拦截到。对应的异常类执行对应的方法,如果都没有匹配到异常类,则采用近亲匹配的方式。
- ResponseBodyAdvice接口的beforeBodyWrite方法:在Respons返回前进行处理,设置成统一的对象再返回。
@ControllerAdvice(assignableTypes = GroupMapperController2.class)
public class ControllerResultWrapper implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
RestResponse restResponse=new RestResponse();
restResponse.setSuccess(true);
restResponse.setMsg("成功");
restResponse.setData(body);
return restResponse;
}
@ExceptionHandler(Exception.class)
public Object handleException(Exception e){
RestResponse restResponse=new RestResponse();
restResponse.setSuccess(false);
restResponse.setMsg(e.getMessage());
return restResponse;
}
}