spring:基于SimpleModule实现动态管理jackson的序列化器(JsonSerializer)和反序列化器(JsonDeserializer)

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字段的实例增加到
ObjectMapperSerializerFactory实例(_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)字段。
DeserializerCacheflushCachedDeserializers()方法可以清除缓存。但问题是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();
		}
	}
}
  • 27
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Security OAuth2中,缓存的处理可以使用Jackson进行序列化。具体实现步骤如下: 1. 添加Jackson依赖 在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> ``` 2. 实现序列化 创建一个自定义的序列化,继承自`JsonSerializer`: ```java public class OAuth2AccessTokenSerializer extends JsonSerializer<OAuth2AccessToken> { @Override public void serialize(OAuth2AccessToken token, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException { gen.writeStartObject(); gen.writeStringField("value", token.getValue()); gen.writeNumberField("expiration", token.getExpiration().getTime()); // 其他字段序列化 gen.writeEndObject(); } } ``` 3. 注册序列化 在配置类中注册序列化: ```java @Configuration public class JacksonConfiguration { @Bean public ObjectMapper objectMapper() { ObjectMapper objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.addSerializer(OAuth2AccessToken.class, new OAuth2AccessTokenSerializer()); objectMapper.registerModule(module); return objectMapper; } } ``` 4. 配置缓存 在缓存配置类中,指定序列化: ```java @Configuration @EnableCaching public class CacheConfiguration extends CachingConfigurerSupport { @Autowired private ObjectMapper objectMapper; @Bean public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new Jackson2JsonRedisSerializer<>(OAuth2AccessToken.class, objectMapper))); return RedisCacheManager.builder(connectionFactory) .cacheDefaults(config) .build(); } } ``` 这样就可以使用Jackson进行缓存的序列化了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值