背景
FastJson和Jackson的序列化器缺存在缺陷, 不盡如人意, 在結(jié)合@Cacheable注解中反序列化可能會(huì)失敗, 或者在原來(lái)的json中夾雜中很多無(wú)用的類(lèi)型信息, 導(dǎo)致json格式的字符串無(wú)法通用, 或者解析失敗等等;
接下來(lái)舉例說(shuō)明一下對(duì)應(yīng)序列化器對(duì)應(yīng)會(huì)遇到的問(wèn)題, 并實(shí)現(xiàn)一個(gè)自定義的通用對(duì)象序列化器. maven和application.yml的配置可參考之前的 文章
示例代碼
@RestController
public class Web {
@Cacheable(value = "cache_list")
@GetMapping("list")
public List<SimpleBook> list() {
ArrayList<SimpleBook> books = new ArrayList<>();
books.add(new SimpleBook());
books.add(new SimpleBook());
return books;
}
@Cacheable(value = "cache_get")
@GetMapping("get")
public SimpleBook get() {
return new SimpleBook();
}
}
public class SimpleBook {
private String name = "book1";
private Double price = 123.4D;
//省略get set
}
Redis緩存管理配置
@EnableCaching
@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Override
@Bean
public CacheManager cacheManager() {
RedisSerializer<Object> redisSerializer = new GenericJackson2JsonRedisSerializer();
//輪流測(cè)試
//redisSerializer = new GenericFastJsonRedisSerializer();
//redisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//redisSerializer = new FastJsonRedisSerializer(Object.class);
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer));
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
return RedisCacheManager.builder(redisCacheWriter).cacheDefaults(defaultCacheConfig).build();
}
}
缺陷
FastJsonRedisSerializer和Jackson2JsonRedisSerializer
這兩個(gè)序列化器在進(jìn)行非集合的緩存操作, 會(huì)報(bào)類(lèi)型轉(zhuǎn)換異常, 即調(diào)用上述代碼中的get()方法.但在調(diào)用list()方法的時(shí)候確是正常的, 且是常規(guī)的json字符串
FastJson報(bào)
java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to com.cache.demo.SimpleBookJackson報(bào)
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.cache.demo.SimpleBook



GenericFastJsonRedisSerializer和GenericJackson2JsonRedisSerializer
-
GenericJackson2JsonRedisSerializer序列化后的數(shù)據(jù)攜帶了類(lèi)型的信息
@class, 同時(shí)為非json格式字符串; 當(dāng)json的工具不一樣時(shí)會(huì)導(dǎo)致解析失敗
GenericFastJsonRedisSerializer格式 -
GenericFastJsonRedisSerializer和上面的類(lèi)似, 不同的時(shí)序列化后的數(shù)據(jù)是json格式的, 但是對(duì)于Double和Float類(lèi)型的, 會(huì)攜帶D和F這些標(biāo)識(shí), 同樣導(dǎo)致無(wú)法通用
GenericFastJsonRedisSerializer格式
改進(jìn)
綜合上述的問(wèn)題, 究其原因是沒(méi)有對(duì)@Cacheable方法上的類(lèi)型進(jìn)行綁定, 所以改進(jìn)的思路是自定義一個(gè)序列化器, 掃描注解上的返回類(lèi)型進(jìn)行一一對(duì)應(yīng)的解析
自定義的序列化器
- Jackson
public class JacksonRedisSerializer<T> implements RedisSerializer<T> {
private final Type type;
private final ObjectMapper objectMapper = new ObjectMapper();
public JacksonRedisSerializer(Type type) {
this.type = type;
}
@Override
public byte[] serialize(T t) throws SerializationException {
try {
return objectMapper.writeValueAsBytes(t);
} catch (JsonProcessingException e) {
throw new SerializationException("serialize fail", e);
}
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
try {
JavaType javaType = objectMapper.constructType(type);
return objectMapper.readValue(bytes, javaType);
} catch (Exception e) {
throw new SerializationException("deserialize by type fail", e);
}
}
}
- FastJson
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
private final Type type;
public FastJsonRedisSerializer(Type type) {
this.type = type;
}
@Override
public byte[] serialize(T t) throws SerializationException {
return JSON.toJSONBytes(t);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
return JSON.parseObject(bytes,type);
}
}
配置
@EnableCaching
@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Autowired
private ApplicationContext applicationContext;
@Override
@Bean
public CacheManager cacheManager() {
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new JacksonRedisSerializer<>(Object.class)));
return RedisCacheManager.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(defaultCacheConfig)
.withInitialCacheConfigurations(buildInitCaches())
.build();
}
private Map<String, RedisCacheConfiguration> buildInitCaches() {
HashMap<String, RedisCacheConfiguration> cacheConfigMap = new HashMap<>();
Arrays.stream(applicationContext.getBeanNamesForType(Object.class))
.map(applicationContext::getType).filter(Objects::nonNull)
.forEach(clazz -> {
ReflectionUtils.doWithMethods(clazz, method -> {
ReflectionUtils.makeAccessible(method);
Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);
if (Objects.nonNull(cacheable)) {
for (String cache : cacheable.cacheNames()) {
RedisSerializationContext.SerializationPair<Object> sp = RedisSerializationContext.SerializationPair
.fromSerializer(new JacksonRedisSerializer<>(method.getGenericReturnType()));
cacheConfigMap.put(cache, RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(sp));
}
}
});
}
);
return cacheConfigMap;
}
}
重復(fù)運(yùn)行接口不報(bào)錯(cuò), 且顯示格式正常則測(cè)試通過(guò)

