在SpringBoot整合redis的时候写使用RedisTemplate来操作缓存数据库redis,结果测试的时候报错。
org.springframework.data.redis.serializer.SerializationException: Could not read JSON: Cannot construct instance of `com.wip.redis.dao.entity.User` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (byte[])"["com.wip.redis.dao.entity.User",{"id":59,"username":"admin","password":"123456"}]"; line: 1, column: 35]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.wip.redis.dao.entity.User` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (byte[])"["com.wip.redis.dao.entity.User",{"id":59,"username":"admin","password":"123456"}]"; line: 1, column: 35]
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.wip.redis.dao.entity.User` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (byte[])"["com.wip.redis.dao.entity.User",{"id":59,"username":"admin","password":"123456"}]"; line: 1, column: 35]
Redis配置文件,由于redisTempate序列化使用的jdk Serializable,存储二进制字节码,所以自定义序列化类。
/**
* redisTemplate 序列化使用的jdk Serializable,存储二进制字节码,所以自定义序列化类
* @param redisConnectionFactory redis连接工厂类
* @return redisTemplate
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用 Jackson2JsonRedisSerializer 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和key的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
实体类加上一个无参的构造方法即可成功运行。
public User() {
}
原因是我在该实体类中添加了一个为了方便实例化该类用的构造函数,导致JVM不会添加默认的无参构造函数,而jackson的反序列化需要无参构造函数,因此报错。
通过debug,让我们来看这一部分的源码:
Jackson中有个BeanDeserializerBase类,该抽象类的子类BeanDeserializer包含了将字节流反序列化的一系列方法:如deserializeFromObject(JsonParser p, DeserializationContext ctxt)、deserialize(JsonParser p, DeserializationContext ctxt)。
在deserializeFromObject方法中,有这么一条判断语句:
if (this._nonStandardCreation)
该属性_nonStandardCreation在BeanDeserializerBase类中是这样定义的:
this._nonStandardCreation = this._unwrappedPropertyHandler != null || this._valueInstantiator.canCreateUsingDelegate() ||
this._valueInstantiator.canCreateUsingArrayDelegate() || this._valueInstantiator.canCreateFromObjectWith() || !this._valueInstantiator.canCreateUsingDefault();
我们把目光放到 this._valueInstantiator.canCreateUsingDefault()上:
public boolean canCreateUsingDefault() {
return this._defaultCreator != null;
}
_defaultCreator表示了该实体类是否含有默认无参构造函数(通过反射机制获得)。
上面提到了,我们添加了新的构造函数,JVM并未给该实体类添加默认无参构造函数,因此_defaultCreator为null。回到最初的判断语句,这里的this._nonStandardCreation便是true。
当没有无参构造函数,BeanDeserializer会怎么处理呢?
它会尝试查找该类是否拥有_unwrappedPropertyHandler或者_externalTypeIdHandler,当然并没有。那么调用deserializeFromObjectUsingNonDefault方法进行deserialize。
protected Object deserializeFromObjectUsingNonDefault(JsonParser p, DeserializationContext ctxt) throws IOException {
JsonDeserializer<Object> delegateDeser = this._delegateDeserializer();
if (delegateDeser != null) {
return this._valueInstantiator.createUsingDelegate(ctxt, delegateDeser.deserialize(p, ctxt));
} else if (this._propertyBasedCreator != null) {
return this._deserializeUsingPropertyBased(p, ctxt);
} else {
Class<?> raw = this._beanType.getRawClass();
return ClassUtil.isNonStaticInnerClass(raw) ? ctxt.handleMissingInstantiator(raw, (ValueInstantiator)null, p, "can only instantiate non-static inner class by using default, no-argument constructor", new Object[0]) : ctxt.handleMissingInstantiator(raw, this.getValueInstantiator(), p, "cannot deserialize from Object value (no delegate- or property-based Creator)", new Object[0]);
}
}
在该方法中它判断了两个属性:_delegateDeserializer和_propertyBasedCreator,对于这两个属性我也不是很明白。通过查阅API文档,前者表示委托反序列化Deserializer that is used if delegate-based creator is to be used for deserializing from JSON Object.,后者表示If the bean needs to be instantiated using constructor or factory method that takes one or more named properties as argument(s), this creator is used for instantiation(?)。最后判断一下是否是内部静态类,进入异常处理部分。
参考:https://www.cnblogs.com/yucfeng/p/8932089.html
最后附上:SpringBoot整合全家桶 欢迎star
谢谢你的支持。