最近项目里面做了一个基于接口和服务的序列化功能,过程中遇到一个问题,从缓存中获取的缓存JSON字符串,反序列化时报错,原因是className包含$$ENHANCECGLIB字样,具体错误如下:
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.data.redis.serializer.SerializationException:
Could not write JSON: No serializer found for class com.***.common.utils.BeanCopyUtil$MethodInterceptorImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
(through reference chain: com.***.spi.dto.***.CurrentStatusDTO["legCrossNodeList"]->java.util.ArrayList[0]->com.***.spi.dto.common.MapNodeDTO$$EnhancerByCGLIB$$b691461c["CGLIB$CALLBACK_0"]);
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
No serializer found for class com.***.common.utils.BeanCopyUtil$MethodInterceptorImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
(through reference chain: com.***.spi.dto.***.CurrentStatusDTO["legCrossNodeList"]->java.util.ArrayList[0]->com.***.spi.dto.common.MapNodeDTO$$EnhancerByCGLIB$$b691461c["CGLIB$CALLBACK_0"])
at org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer.serialize(Jackson2JsonRedisSerializer.java:88)
at org.springframework.data.redis.core.AbstractOperations.rawValue(AbstractOperations.java:127)
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:254)
at com.***.cache.implement.RedisCacher.putValue(RedisCacher.java:211)
at com.***.cache.implement.RedisCacher.loaderAndPutValue(RedisCacher.java:177)
110 more
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.***.common.utils.BeanCopyUtil$MethodInterceptorImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.***.spi.dto.***.CurrentStatusDTO["legCrossNodeList"]->java.util.ArrayList[0]->com.***.spi.dto.common.MapNodeDTO$$Enhan
初步怀疑是Redis序列化的问题,检查并调整redisTemplateConfig的配置,无效。
public RedisTemplate redisTemplate(JedisConnectionFactory connectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(connectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE,false);
objectMapper.configure(DeserializationFeature.FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY,false);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
objectMapper.registerModule(new JavaTimeModule());
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(redisTemplate.getKeySerializer());
// redisTemplate.setEnableTransactionSupport(true);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
跟踪接口发现缓存之前对象就已经出现问题,推测试mybatis返回的问题,再跟,发现mybatis查询后即时返回的数据是正常的,原来是代码里面自己写的BeanCopy的问题。打开工具类,看了实现:
public static <T> T beanCopy(Object source, Class<T> clazz) {
T target = newCglibInstance(clazz);
beanCopy(source, target);
return target;
}
public static Object newCglibInstance(Class clazz) {
try {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(new BeanCopyUtil.MethodInterceptorImpl());
return enhancer.create();
} catch (Throwable var2) {
log.warn("Can't newInstance class {}", clazz.getSimpleName());
throw new Error(var2.getMessage());
}
}
找到原因之后,改造起来就容易多了,直接用spring自带的copy实现即可。
public static <T> T copyBean(Object source, Class<T> clazz) {
T target = BeanUtils.instantiateClass(clazz);
BeanUtils.copyProperties(source, target);
return target;
}
at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.data.redis.serializer.SerializationException:
Could not write JSON: No serializer found for class com.***.common.utils.BeanCopyUtil$MethodInterceptorImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
(through reference chain: com.***.spi.dto.***.CurrentStatusDTO["legCrossNodeList"]->java.util.ArrayList[0]->com.***.spi.dto.common.MapNodeDTO$$EnhancerByCGLIB$$b691461c["CGLIB$CALLBACK_0"]);
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
No serializer found for class com.***.common.utils.BeanCopyUtil$MethodInterceptorImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
(through reference chain: com.***.spi.dto.***.CurrentStatusDTO["legCrossNodeList"]->java.util.ArrayList[0]->com.***.spi.dto.common.MapNodeDTO$$EnhancerByCGLIB$$b691461c["CGLIB$CALLBACK_0"])
at org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer.serialize(Jackson2JsonRedisSerializer.java:88)
at org.springframework.data.redis.core.AbstractOperations.rawValue(AbstractOperations.java:127)
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:254)
at com.***.cache.implement.RedisCacher.putValue(RedisCacher.java:211)
at com.***.cache.implement.RedisCacher.loaderAndPutValue(RedisCacher.java:177)
... 110 more
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
No serializer found for class com.***.common.utils.BeanCopyUtil$MethodInterceptorImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: com.***.spi.dto.***.CurrentStatusDTO["legCrossNodeList"]->java.util.ArrayList[0]->com.***.spi.dto.common.MapNodeDTO$$Enhan