Module
jackson的(com.fasterxml.jackson.databind.Module)设计作为一个扩展的接口,可以注册到ObjectMapper实例(ObjectMapper.registerModule),为默认ObjectMapper实例提供功能扩展;比如用于定义为数据类型指定序列化和反序列化。
jackson为Module接口提供了一个默认的简单实现(com.fasterxml.jackson.databind.module.SimpleModule),SimpleModule已经很好用了,一般情况下可以直接拿来用。
网上关于jackson使用SimpleModule的例子,差不多都是下面这样的:
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
JsonSerializer<?> serializer=.....
/** 向 Module增加序列化器 */
module.addSerializer(serializer);
/** 将 Module注册到 ObjectMapper实例 */
objectMapper.registerModule(module);
这些例子,给我一个错觉,以为必须要在构造ObjectMapper实例时就要将定义了指定类型的序列化器的Module实例注册到 ObjectMapper实例才能生效。
SimpleModule
最近的工作中,我需要实现能在spring环境下,在服务运行时根据服务方法的不同动态修改数据类型的序列化器。这就让我不得不仔细了解了SimpleModule
的实现。
才明白,jackson的Module
机制是可以支持动态修改Module
实例的,并且只要将Module
注册到ObjectMapper
,再修改Module
实例的结果会同步到ObjectMapper
实例。
还以上面的增加序列化器的过程为例,
SimpleModule
中管理序列化器的字段为_serializers
,类型为SimpleSerializers
ObjectMapper.registerModule
注册Module
的过滤其实就是将_serializers
字段的实例增加到
ObjectMapper
的SerializerFactory
实例(_serializerFactory
字段)
参见下面ObjectMapper.registerModule
方法的实现代码片段
public void addSerializers(Serializers s) {
_serializerFactory = _serializerFactory.withAdditionalSerializers(s);
}
所以可以先将SimpleModule
注册到ObjectMapper
,再调用SimpleModule.addSerializer
方法也是同样可以将JsonSerializer
实例传递给已经注册的ObjectMapper
实例的。
(一)初步方案
所以一个大概的实现方案就有了:
大概致思路就是先向ObjectMapper
实例注册一个空的SimpleModule
实例,
后续就可以如直接向SimpleModule
实例,增加序列化器了。好简单。
ExampleOfSimpleModule.java
@Component
public class ExampleOfSimpleModule {
/**
* [自动注入]spring环境下的ObjectMapper实例
*/
@Autowired
private ObjectMapper objectMapper;
private final SimpleModule module = new SimpleModule();
@PostConstruct
void init() {
/** 先将空SimpleModule实例的注册到系统的ObjectMapper实例 */
objectMapper.registerModule(module);
}
/**
* 向 {@link #module}增加过序列化器
* @param serializer
*/
public void addSerializer(JsonSerializer<?> serializer) {
if(null != serializer) {
module.addSerializer(serializer);
}
}
}
然而实际运行发现这样根本无效。ObjectMapper
在序列化时并没有使用我指定的序列化器。
再仔细看SimpleModule
的源码,发现SimpleModule
的默认构造方法构造的实例 _serializers
为null,
而SimpleModule.setupModule
方法执行时如果_serializers
为null,就不会执行向ObjectMapper
增加序列化器的逻辑
SimpleModule.setupModule
方法代码片段:
public void setupModule(SetupContext context)
{
if (_serializers != null) {
context.addSerializers(_serializers);
}
/..........///
}
(二)改进方案
所以上面方案示例代码ExampleOfSimpleModule的void init()
方法代码要改一下:
调用SimpleModule.setSerializers
方法为_serializers
定义一个非null的SimpleSerializers实例
@PostConstruct
void init() {
/**
* SimpleModule中的 _serializers字段默认为空,如果不事先调用setter方法指定非空实例,
* 则执行 ObjectMapper.registerModule方法无效,
* 后续动态增加的序列化和反序列化器也无效
*/
module.setSerializers( new SimpleSerializers());
objectMapper.registerModule(module);
}
(三)改进方案
经过改进,ObjectMapper
注册Module
无效的问题总算是解决了,但实际服务运行时,还是没有使用我指定的序列化器。
这次要深入看ObjectMapper
的源码了:
ObjectMapper
获取类型的序列化器是通过_serializerProvider
字段的SerializerProvider
实例的findTypedValueSerializer
方法来获取的:
如下是SerializerProvider.findTypedValueSerializer(Class<?> valueType, boolean cache, BeanProperty property)
方法代码片段:
public JsonSerializer<Object> findTypedValueSerializer(Class<?> valueType,
boolean cache, BeanProperty property)
throws JsonMappingException
{
// Two-phase lookups; local non-shared cache, then shared:
JsonSerializer<Object> ser = _knownSerializers.typedValueSerializer(valueType);
if (ser != null) {
return ser;
}
......
}
SerializerProvider
有个final
的_knownSerializers
字段,它就类似一个缓存。
findTypedValueSerializer
首先会从_knownSerializers
字段查找已知的序列化器,如果找到就直接返回,根本不会从SerializerFactory
获取(前面我们知道Module
中定义的序列化器是保存到了SerializerFactory
对象)
进一步在SerializerProvider
的构造方法中找到了_knownSerializers
字段的来源,它来源于_serializerCache
字段,果然看名字就知道这_serializerCache
字段也是一个类型为SerializerCache
的缓存,
SerializerProvider构造方法代码片段
protected SerializerProvider(SerializerProvider src,
SerializationConfig config, SerializerFactory f)
{
_serializerFactory = f;
_config = config;
_serializerCache = src._serializerCache;
//。。。。/
_knownSerializers = _serializerCache.getReadOnlyLookupMap();
}
SerializerCache
缓存是可以清除的
如下为SerializerCache
清除缓存的方法
public synchronized void flush() {
_sharedMap.clear();
}
如果我增加了序列化器后,能够序列化器清除缓存,那么findTypedValueSerializer
方法的逻辑就会继续往下走,就应该会去查找我在SimpleModule
中定义的序列化器了。
进一步查看了DefaultSerializerProvider
的源码,
哎呀,真是瞌睡时遇到枕头,
它果然有一个方法flushCachedSerializers()
可以清除_serializerCache
字段缓存
如下:
public void flushCachedSerializers() {
_serializerCache.flush();
}
现在问题应该就解决了,就是在向SimpleModule
增加序列化器之后,就调用DefaultSerializerProvider.flushCachedSerializers()
方法清除缓存,这样增加的序列化器应该就能生效了。
所以上面方案示例代码ExampleOfSimpleModule的addSerializer
法代码要改一下:
public void addSerializer(JsonSerializer<?> serializer) {
if(null != serializer) {
module.addSerializer(serializer);
/** 清除 DefaultSerializerProvider中的序列化器缓存,否则增加的过滤器不一定能生效 */
DefaultSerializerProvider prov= (DefaultSerializerProvider) objectMapper.getSerializerProvider();
prov.flushCachedSerializers();
}
}
重新运行测试,果然生效。
addDeserializer
有了上面的经验,我又如法炮制,研究了增加反序列化器的方法。发现
DeserializationContext
同样有一个名为_cache
的反序列化器缓存(DeserializerCache
)字段。
DeserializerCache
有flushCachedDeserializers()
方法可以清除缓存。但问题是DeserializationContext
却没有方法可以调用_cache
字段的这个方法来执行缓存清除动作,而_cache
为保护字段(proteced)不能直接访问。
看来jackson的序列化和反序列化模块应该是不同的作者维护的。
我当前使用的jackson版本是2.12.5,我以为高版本会增加这样的方法,但是我查了最新版本,仍然没有,好吧,只能另外想办法了。
于是我在com.fasterxml.jackson.databind
包下写了类DeserializeContextCacheCleaner
,继承DeserializationContext
。只为调用_cache.flushCachedDeserializers()
DeserializeContextCacheCleaner.java
package com.fasterxml.jackson.databind;
/**
* only for clear cache in {@link DeserializationContext}
* @author guyadong
*
*/
@SuppressWarnings("serial")
public abstract class DeserializeContextCacheCleaner extends DeserializationContext{
private DeserializeContextCacheCleaner(DeserializationContext src) {
super(src);
}
/**
* 清除{@link DeserializationContext#_cache}字段缓存
*/
public static void clearCache(DeserializationContext src) {
if(null != src) {
src._cache.flushCachedDeserializers();
}
}
}
有了DeserializeContextCacheCleaner
支持,向SimpleModule
增加反序列化器就简单了:
/**
* 向 {@link #module}增加过反序列化器
* @param deserializer
*/
@SuppressWarnings("unchecked")
public void addDeserializer(JsonDeserializer<?> deserializer) {
if(null != deserializer) {
module.addDeserializer((Class<Object>)(deserializer).handledType(), deserializer);
/** 清除 DeserializationContext中的反序列化器缓存,否则增加的过滤器不一定能生效 */
DeserializeContextCacheCleaner.clearCache(objectMapper.getDeserializationContext());
}
}
完整方案:
好了上面零零碎碎写了好多代码片段,现在把它们整理出来就是完整的动态管理jackson的序列化器和反序列化器的实现方案示例代码:
ExampleOfSimpleModule.java
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.DeserializeContextCacheCleaner;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleDeserializers;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.module.SimpleSerializers;
import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider;
/**
* 动态管理序列化器和反序列化器实现
* @author guyadong
*/
@Component
public class ExampleOfSimpleModule {
/**
* [自动注入]spring环境下的ObjectMapper实例
*/
@Autowired
private ObjectMapper objectMapper;
private final SimpleModule module = new SimpleModule();
@PostConstruct
void init() {
/**
* SimpleModule中的 _serializers和 _deserializers字段默认为空,如果不事先调用setter方法指定非空实例,
* 则执行 ObjectMapper.registerModule方法无效,
* 后续动态增加的序列化和反序列化器也无效
*/
module.setSerializers( new SimpleSerializers());
module.setDeserializers(new SimpleDeserializers());
objectMapper.registerModule(module);
}
/**
* 向 {@link #module}增加过反序列化器
* @param deserializer
*/
@SuppressWarnings("unchecked")
public void addDeserializer(JsonDeserializer<?> deserializer) {
if(null != deserializer) {
module.addDeserializer((Class<Object>)(deserializer).handledType(), deserializer);
/** 清除 DeserializationContext中的反序列化器缓存,否则增加的过滤器不一定能生效 */
DeserializeContextCacheCleaner.clearCache(objectMapper.getDeserializationContext());
}
}
/**
* 向 {@link #module}增加过序列化器
* @param serializer
*/
public void addSerializer(JsonSerializer<?> serializer) {
if(null != serializer) {
module.addSerializer(serializer);
/** 清除 DefaultSerializerProvider中的序列化器缓存,否则增加的过滤器不一定能生效 */
DefaultSerializerProvider prov= (DefaultSerializerProvider) objectMapper.getSerializerProvider();
prov.flushCachedSerializers();
}
}
}