有道无术,术尚可求,有术无道,止于术。
本系列Jackson 版本 2.17.0
源码地址:https://gitee.com/pearl-organization/study-jaskson-demo
文章目录
- 1. 前言
- 2. Spring Web
- 3. Jackson2ObjectMapperBuilder
- 4. Jackson2ObjectMapperFactoryBean
- 5. MappingJackson2HttpMessageConverter
1. 前言
Spring生态使用Jackson作为默认的JSON处理框架,这些年随着Spring的大发异彩和Jackson本身的优越特性,已成为世界上最流行的JSON库。
接下来,本系列会讲解Spring MVC、Spring Boot中如何使用Jackson,以及Spring对其进行扩展增强的相关源码,所以需要读者有一定的Spring、Spring Boot开发经验。
2. Spring Web
Spring MVC对于后端开发人员来说已经很熟悉了,它是一个构建在Servlet之上的一个Web框架,而Spring Web是Spring MVC的基础模块(还有一个Spring Flux ),它们同属于spring-framework框架。
Web框架在处理HTTP请求响应时,需要将请求报文反序列化为Java对象进行接收处理,在响应阶段,需要将Java对象反序列化为报文写出,所以需要依赖JSON框架去处理。
Spring Web中可以看到引入了Jackson、GJson:

在spring-web模块的org.springframework.http.converter.json包下,可以看到Json集成的代码:

集成Jackson的主要有:
Jackson2ObjectMapperBuilder:ObjectMapper构建器Jackson2ObjectMapperFactoryBean:FactoryBean创建和管理ObjectMapper对象MappingJackson2HttpMessageConverter:基于Jackson的消息转换器SpringHandlerInstantiator:Spring容器创建Jackson组件,例如JsonSerializer、JsonDeserializer、KeyDeserializer…JacksonModulesRuntimeHints:Spring AOT所需的RuntimeHints(运行时提示)
3. Jackson2ObjectMapperBuilder
Jackson2ObjectMapperBuilder从名字上看已经很好理解,它是一个Jackson的ObjectMapper构建器,提供了Fluent API来定制ObjectMapper的默认属性并构建实例。
在之前我们都是通过new的方式创建ObjectMapper实例,现在可以使用构建者模式,这也是Spring自身使用和推荐使用的方式。
它的构造方法是public的,说明可以直接new创建Jackson2ObjectMapperBuilder:
public Jackson2ObjectMapperBuilder() {}
提供了多个创建不同数据类型支持的静态方法:
// 构建 Jackson2ObjectMapperBuilderpublic static Jackson2ObjectMapperBuilder json() {return new Jackson2ObjectMapperBuilder();}// 构建 Jackson2ObjectMapperBuilder,并设置需要创建 XmlMapper,需要引入`jackson-dataformat-xml`模块,用于支持`XML`数据格式public static Jackson2ObjectMapperBuilder xml() {return new Jackson2ObjectMapperBuilder().createXmlMapper(true);}// 指定 JsonFactory-》SmileFactory,需要引入`jackson-dataformat-smile`模块,用于支持`Smile`数据格式public static Jackson2ObjectMapperBuilder smile() {return new Jackson2ObjectMapperBuilder().factory(new SmileFactoryInitializer().create());}// 指定 JsonFactory-》CBORFactory-》需要引入`jackson-dataformat-cbor`模块,用于支持`CBOR`数据格式public static Jackson2ObjectMapperBuilder cbor() {return new Jackson2ObjectMapperBuilder().factory(new CborFactoryInitializer().create());}
一般场景中,使用json()创建即可:
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
此外还提供了很多方法,例如自定义序列化/反序列化器、模块注册、启用/禁用特征、 混合注解等等,和ObjectMapper 本身的方法大多类似,这里就不一一赘述了。
// 配置自定义序列化器(多个)public Jackson2ObjectMapperBuilder serializers(JsonSerializer<?>... serializers) {for (JsonSerializer<?> serializer : serializers) {Class<?> handledType = serializer.handledType();if (handledType == null || handledType == Object.class) {throw new IllegalArgumentException("Unknown handled type in " + serializer.getClass().getName());}this.serializers.put(serializer.handledType(), serializer);}return this;}// 模块注册public Jackson2ObjectMapperBuilder modules(Module... modules) {return modules(Arrays.asList(modules));}/*** 启用特征** @see com.fasterxml.jackson.core.JsonParser.Feature* @see com.fasterxml.jackson.core.JsonGenerator.Feature* @see com.fasterxml.jackson.databind.SerializationFeature* @see com.fasterxml.jackson.databind.DeserializationFeature* @see com.fasterxml.jackson.databind.MapperFeature*/public Jackson2ObjectMapperBuilder featuresToEnable(Object... featuresToEnable) {for (Object feature : featuresToEnable) {this.features.put(feature, Boolean.TRUE);}return this;}
需要重点关注的方法有build、configure,build方法用于构建一个ObjectMapper实例,每次调用都会返回一个新的对象,对于处理不同JSON格式或需要不同序列化/反序列化行为的场景,可以构建新的实例来处理。
@SuppressWarnings("unchecked")public <T extends ObjectMapper> T build() {// 创建实例ObjectMapper mapper;if (this.createXmlMapper) {// 需要创建XmlMapper,则使用 XmlFactorymapper = (this.defaultUseWrapper != null ?new XmlObjectMapperInitializer().create(this.defaultUseWrapper, this.factory) :new XmlObjectMapperInitializer().create(this.factory));} else {// 不需要创建XmlMapper,则使用指定的工厂mapper = (this.factory != null ? new ObjectMapper(this.factory) : new ObjectMapper());}// 配置configure(mapper);return (T) mapper;}
configure方法用于将成员属性都设置给已存在的ObjectMapper实例:
public void configure(ObjectMapper objectMapper) {Assert.notNull(objectMapper, "ObjectMapper must not be null");// 注册模块MultiValueMap<Object, Module> modulesToRegister = new LinkedMultiValueMap<>();if (this.findModulesViaServiceLoader) {ObjectMapper.findModules(this.moduleClassLoader).forEach(module -> registerModule(module, modulesToRegister));} else if (this.findWellKnownModules : modulesToRegister.values()) {modules.addAll(nestedModules);}objectMapper.registerModules(modules);// 设置时间日期格式化if (this.dateFormat != null) {objectMapper.setDateFormat(this.dateFormat);}// 设置 Localeif (this.locale != null) {objectMapper.setLocale(this.locale);}// 设置 TimeZone if (this.timeZone != null) {objectMapper.setTimeZone(this.timeZone);}// 设置 注解解析器if (this.annotationIntrospector != null) {objectMapper.setAnnotationIntrospector(this.annotationIntrospector);}// 设置属性名称策略if (this.propertyNamingStrategy != null) {objectMapper.setPropertyNamingStrategy(this.propertyNamingStrategy);}// 省略其他.........if (this.configurer != null) {this.configurer.accept(objectMapper);}}
4. Jackson2ObjectMapperFactoryBean
Jackson2ObjectMapperFactoryBean提供了基于Spring框架FactoryBean机制创建和配置ObjectMapper实例。
示例如下:
@Configuration
public class MvcConfig {@Beanpublic Jackson2ObjectMapperFactoryBean jacksonObjectMapper() {Jackson2ObjectMapperFactoryBean factoryBean = new Jackson2ObjectMapperFactoryBean();factoryBean.setIndentOutput(true);factoryBean.setSimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 还可以设置其他属性,如自定义的序列化器、反序列化器等return factoryBean;}
}
5. MappingJackson2HttpMessageConverter
MappingJackson2HttpMessageConverter即基于Jackson2的消息转换器实现,其本身并没有多少代码,读写能力来自于父类。
继承关系如下:
HttpMessageConverterGenericHttpMessageConverterAbstractGenericHttpMessageConverterAbstractJackson2HttpMessageConverterMappingJackson2HttpMessageConverter
顶级接口HttpMessageConverter是Spring Web定义的一个HTTP消息转换器。负责将请求和响应的数据从Java对象转换为HTTP协议所需的格式,或者将HTTP协议中的数据转换为Java对象,是Spring框架中用于处理HTTP请求和响应数据转换的重要组件。
直接父类AbstractJackson2HttpMessageConverter定义了泛型为Object,其内部维护了多个ObjectMapper对象:
public abstract class AbstractJackson2HttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> // 默认的ObjectMapperprotected ObjectMapper defaultObjectMapper;@Nullable// registerObjectMappersForType 方法注册的多个ObjectMapperprivate Map<Class<?>, Map<MediaType, ObjectMapper>> objectMapperRegistrations;
可以调用构造方法或者set方法设置默认的ObjectMapper:
protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper) {this.defaultObjectMapper = objectMapper;DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();prettyPrinter.indentObjectsWith(new DefaultIndenter(" ", "\ndata:"));this.ssePrettyPrinter = prettyPrinter;}protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType supportedMediaType) {this(objectMapper);this.setSupportedMediaTypes(Collections.singletonList(supportedMediaType));}protected AbstractJackson2HttpMessageConverter(ObjectMapper objectMapper, MediaType... supportedMediaTypes) {this(objectMapper);this.setSupportedMediaTypes(Arrays.asList(supportedMediaTypes));}public void setObjectMapper(ObjectMapper objectMapper) {Assert.notNull(objectMapper, "ObjectMapper must not be null");this.defaultObjectMapper = objectMapper;this.configurePrettyPrint();}
重写了父类的canRead、canWrite方法,例如canRead,首先调用父类的 canRead 方法,是否支持当前 MediaType,然后查询并判断ObjectMapper是否支持反序列化当前类型:
public boolean canRead(Type type, @Nullable Class<?> contextClass, @Nullable MediaType mediaType) {// 调用父类的 canRead 方法,是否支持当前 MediaTypeif (!this.canRead(mediaType)) {return false;} else {// 根据JAVA Type 类型,转换为 Jackson 的 JavaType JavaType javaType = this.getJavaType(type, contextClass);// 查询一个可用的 ObjectMapper ObjectMapper objectMapper = this.selectObjectMapper(javaType.getRawClass(), mediaType);if (objectMapper == null) {return false;} else {// ObjectMapper 是否可反序列化当前类型 AtomicReference<Throwable> causeRef = new AtomicReference();if (objectMapper.canDeserialize(javaType, causeRef)) {return true;} else {this.logWarningIfNecessary(javaType, (Throwable)causeRef.get());return false;}}}}
最重要的是真正实现了转换器最核心的读写方法,例如read方法中并不是直接通过ObjectMapper进行读操作,而是通过ObjectMapper创建了底层的ObjectReader 去执行读操作。
// 将 HTTP 输入消息,转换为 JAVA 对象public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {JavaType javaType = this.getJavaType(type, contextClass);return this.readJavaType(javaType, inputMessage);}private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) throws IOException {// 获取 Content-TypeMediaType contentType = inputMessage.getHeaders().getContentType();// 获取 Charset Charset charset = this.getCharset(contentType);// 查询可用的 ObjectMapper ObjectMapper objectMapper = this.selectObjectMapper(javaType.getRawClass(), contentType);Assert.state(objectMapper != null, () -> {return "No ObjectMapper for " + javaType;});boolean isUnicode = ENCODINGS.containsKey(charset.name()) || "UTF-16".equals(charset.name()) || "UTF-32".equals(charset.name());try {// 获取输入流 InputStream inputStream = StreamUtils.nonClosing(inputMessage.getBody());if (inputMessage instanceof MappingJacksonInputMessage) {// 支持 Jackson 视图 MappingJacksonInputMessage mappingJacksonInputMessage = (MappingJacksonInputMessage)inputMessage;Class<?> deserializationView = mappingJacksonInputMessage.getDeserializationView();if (deserializationView != null) {ObjectReader objectReader = objectMapper.readerWithView(deserializationView).forType(javaType);objectReader = this.customizeReader(objectReader, javaType);if (isUnicode) {return objectReader.readValue(inputStream);}Reader reader = new InputStreamReader(inputStream, charset);return objectReader.readValue(reader);}}// 使用 `ObjectMapper` 底层的`ObjectReader`读取ObjectReader objectReader = objectMapper.reader().forType(javaType);objectReader = this.customizeReader(objectReader, javaType);if (isUnicode) {return objectReader.readValue(inputStream);} else {Reader reader = new InputStreamReader(inputStream, charset);return objectReader.readValue(reader);}} catch (InvalidDefinitionException var12) {throw new HttpMessageConversionException("Type definition error: " + var12.getType(), var12);} catch (JsonProcessingException var13) {throw new HttpMessageNotReadableException("JSON parse error: " + var13.getOriginalMessage(), var13, inputMessage);}}