文章目录
#Spring boot 2.0 升级到 3.3.1 的相关问题 (四)
Jackson的配置问题
问题介绍
MappingJackson2HttpMessageConverter
没有默认编码的问题
在Spring Boot 3.3.1 自定配置创建的MappingJackson2HttpMessageConverter
默认是没有指定字符串编码,因此在实际的使用中会自动使用ISO8859-1
的编码。
StringHttpMessageConverter
默认编码为ISO8859-1
,这个也需要修改。
自定义com.fasterxml.jackson.databind.Module
bean 不生效的问题
由于Spring 自动配置的MappingJackson2HttpMessageConverter
创建时并不会使用配置的com.fasterxml.jackson.databind.Module
,因此会导致原型2.0版本通过该方式配置的JSON自定义序列化和反序列化无效。
问题解决
方案1
增加一个配置类实现WebMvcConfigurer
接口,并实现configureMessageConverters
或者extendMessageConverters
方法。这个两个方法都会将当前的系统中的所有MessageConverters
传入。区别在于extendMessageConverters
是做后置加工的,也就是到执行它的时候,系统内的MessageConverters
已经是被前置处理过的(前置操作包括合并、排序等),而configureMessageConverters
是没有被前置处理,甚至是部分的MessageConverters
。
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
* 自定义的Mvc配置,用于配置转换器
*/
@Configuration
public class CustomWebMvcConverterConfigurer implements WebMvcConfigurer {
@Autowired
private com.fasterxml.jackson.databind.Module customJackSonModule;
/**
* 扩展消息转换器列表,以支持自定义的字符集和Jackson模块。
* 此方法遍历给定的消息转换器列表,并对特定类型的转换器应用自定义配置。
* 具体来说,它将字符串转换器的默认字符集设置为UTF-8,并为Jackson转换器注册一个自定义的Jackson模块。
*
* @param converters 消息转换器的列表,这里我们将遍历这个列表并对特定的转换器进行配置。
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// 遍历给定的消息转换器列表
for(HttpMessageConverter<?> converter : converters) {
// 如果当前转换器是字符串转换器,则将其默认字符集设置为UTF-8
if(converter instanceof StringHttpMessageConverter stringHttpMessageConverter) {
stringHttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
continue;
}
// 如果当前转换器是Jackson 2转换器,则将其默认字符集设置为UTF-8,并注册自定义的Jackson模块
if(converter instanceof MappingJackson2HttpMessageConverter jackson2HttpMessageConverter) {
jackson2HttpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
ObjectMapper objectMapper = jackson2HttpMessageConverter.getObjectMapper();
// 注册自定义的Jackson模块,以支持额外的序列化和反序列化需求
objectMapper.registerModule(customJackSonModule);
}
}
}
}
方案2
直接在创建一个MappingJackson2HttpMessageConverter
Bean。
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.nio.charset.StandardCharsets;
@Configuration
public class JacksonConfiguration {
@Autowired
private com.fasterxml.jackson.databind.Module customJackSonModule;
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
// 注册自定义Module
objectMapper.registerModule(customJackSonModule);
converter.setObjectMapper(objectMapper);
//默认字符集设置为UTF-8
converter.setDefaultCharset(StandardCharsets.UTF_8);
return converter;
}
} }
}
方案3 (不可行)
自定义Jackson2ObjectMapperBuilderCustomizer
,用于配置Jackson2ObjectMapperBuilder
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
/**
* 配置Jackson2ObjectMapperBuilderCustomizer
*/
@Configuration
public class JacksonConfiguration {
@Autowired
private com.fasterxml.jackson.databind.Module customJackSonModule;
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperBuilderCustomizer() {
return new Jackson2ObjectMapperBuilderCustomizer() {
@Override
public void customize(Jackson2ObjectMapperBuilder builder) {
// 注册自定义的 Module
builder.modulesToInstall(customJackSonModule);
}
};
}
}
该方案不可行的原因是Spring Boot自动配置的MappingJackson2HttpMessageConverter
使用的ObjectMapper
就没使用Jackson2ObjectMapperBuilder
的Bean去创建的,而是每次都重新创建一个Jackson2ObjectMapperBuilder
的创建的。 这个应该是给未来版本来解决的,详情可以看org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration
这个类。
相关资料
MappingJackson2HttpMessageConverter
转换器构造流程
几个构造流程:
1、org.springframework.boot.autoconfigure.BackgroundPreinitializer
的org.springframework.boot.autoconfigure.BackgroundPreinitializer.MessageConverterInitializer
初始化AllEncompassingFormHttpMessageConverter
消息转换器。在这个消息转换器中默认会初始化一堆消息转换器,其中就包含MappingJackson2HttpMessageConverter
的无参构造方法。
2、org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration#formContentFilter
方法中创建了一个OrderedFormContentFilter
的Filter,这个Filter中默认会创建一个AllEncompassingFormHttpMessageConverter
消息转换器。在这个消息转换器中默认会初始化一堆消息转换器,其中就包含MappingJackson2HttpMessageConverter
的无参构造方法。
AllEncompassingFormHttpMessageConverter
只处理Form 表单请求(请求头为application/x-www-form-urlencoded
)的转换。
3、org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration#mappingJackson2HttpMessageConverter
,使用指定的ObjectMapper
创建了一个MappingJackson2HttpMessageConverter
4、org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#requestMappingHandlerAdapter
-> org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#getMessageConverters
->
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#addDefaultHttpMessageConverters
使用默认的Jackson2ObjectMapperBuilder
的配置创建一个MappingJackson2HttpMessageConverter
5、org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration#messageConverters
其他
默认的Jackson2ObjectMapperBuilderCustomizer
的实现
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.Jackson2ObjectMapperBuilderCustomizerConfiguration.StandardJackson2ObjectMapperBuilderCustomizer
HttpMessageConverter
的write
方法调用点
org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters(T, org.springframework.core.MethodParameter, org.springframework.http.server.ServletServerHttpRequest, org.springframework.http.server.ServletServerHttpResponse)