不知道有没有安卓开发的小伙伴跟我一样,平时喜欢维护一下自己的小项目,有余力的情况下自己把后端也写了。在安卓开发中越用越顺手的我这次的后端开发就开始使用了Kotlin,与Springboot搭配网上那是说的神乎其技。我也信以为真,因为我也非常喜欢Kotlin,于是边着手了旧项目的重构+迁移。
一切都很顺利,毕竟IDEA可以一键转换Kotlin,加上自己对Kotlin的掌握程度,转换不成问题,直到Redis时,有这么一句:
@Autowired
private lateinit var redisTemplate: RedisTemplate<String, Any>
运行时这句代码报了错,内容如下:
Description:
Field redisTemplate in com.gitlab.nykbapi.util.RedisUtil required a bean of type 'org.springframework.data.redis.core.RedisTemplate' that could not be found.
The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:Consider defining a bean of type 'org.springframework.data.redis.core.RedisTemplate' in your configuration.
意思大致就是说redisTemplate找不到bean,我心想不对呀,这个项目在Java时是正常跑的,怎么来到Kotlin就出问题了,于是我上网寻找问题答案,看到了这么一个建议(原文连接):
好家伙,简单!急性子的我看到解决办法立马就去试了,能跑,没问题,关闭浏览器!后文直接被我忽略...
程序能跑,问题解决了吗,真的解决了吗?
在我兴高采烈的写了一堆代码后,发现了一个问题:
没错,存储redis时自动给我的每个字段前都加上了一段\xac\xed\x00\x05t\x00\的字符,眼尖的我发现了“HEX”的标签,我心想哦~没事,切换成16进制了而已,于是我将它切成了text:
WTF?乱码无疑了,又开始了上网“Redis乱码”、“Redis自动加上乱码前缀”、“Redis序列化”....乱七八糟的,直到一句话映入我眼帘:
你确定你的Bean有被找到?
好家伙,这才想起我的注解改成了@Resource ,心中一万个草泥马...
将注解改回@Autowired后,又要面对先前的问题:
Field redisTemplate in com.gitlab.nykbapi.util.RedisUtil required a bean of type 'org.springframework.data.redis.core.RedisTemplate' that could not be found.
我一拍脑门,才想起忘记将我的RedisConfig拷贝过来了,真糊涂啊!
一顿CV操作(注意:RedisConfig由于我急于验证,所以并未转换成Kotlin):
package cc.han0.nykbcommon.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @author Han0
* @date 2022/8/15 17:01
*/
@Configuration
public class RedisConfig {
@Bean
public static RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//创建对象映射器
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//设置value的序列化对
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//设置value的序列化器
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
//创建对象映射器
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
//设置value的序列化对
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
//设置key序列化器
redisCacheConfiguration.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));
//设置value序列化器
// redisCacheConfiguration.serializeValuesWith(jackson2JsonRedisSerializer);
return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(redisCacheConfiguration).build();
}
}
大功告成,运行!
成功运行!
再走一遍接口:
WTF?阿真,你来真的?
想了想不应该呀,之前确实是没问题的,代码都没变,唯一变的就是Redis版本变高了,于是又开始降了一堆级,还是不行
直到我看到了这么一篇文章:
springboot的RedisTemplate泛型自动注入问题_redistemplate 泛型-CSDN博客
简单说就是注入会受泛型影响,突然想起RedisConfig是Java写的,泛型写的是<String, Object>,而这边RedisUtils写的是<String, Any>,emmmmm...
真的引起我的浑身不适!因为这个语言问题,害我排查了一晚!(归根结底其实是我太菜...)
键来!
package com.gitlab.nykbapi.config
import com.fasterxml.jackson.annotation.JsonAutoDetect
import com.fasterxml.jackson.annotation.JsonTypeInfo
import com.fasterxml.jackson.annotation.PropertyAccessor
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.cache.RedisCacheConfiguration
import org.springframework.data.redis.cache.RedisCacheManager
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext
import org.springframework.data.redis.serializer.StringRedisSerializer
/**
* @author Han0
* @date 2022/8/15 17:01
*/
@Configuration
class RedisConfig {
@Bean
fun redisTemplate(redisConnectionFactory: RedisConnectionFactory): RedisTemplate<String, Any> {
val redisTemplate = RedisTemplate<String, Any>()
redisTemplate.setConnectionFactory(redisConnectionFactory)
//设置key的序列化器
redisTemplate.keySerializer = StringRedisSerializer()
redisTemplate.hashKeySerializer = StringRedisSerializer()
//创建对象映射器
val objectMapper = ObjectMapper()
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL)
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
//设置value的序列化对
val jackson2JsonRedisSerializer = Jackson2JsonRedisSerializer(
Any::class.java
)
jackson2JsonRedisSerializer.setObjectMapper(objectMapper)
//设置value的序列化器
redisTemplate.valueSerializer = jackson2JsonRedisSerializer
redisTemplate.hashValueSerializer = jackson2JsonRedisSerializer
redisTemplate.afterPropertiesSet()
return redisTemplate
}
@Bean
fun redisCacheManager(redisConnectionFactory: RedisConnectionFactory?): RedisCacheManager {
//创建对象映射器
val objectMapper = ObjectMapper()
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
objectMapper.activateDefaultTyping(
LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY
)
val redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
//设置key序列化器
redisCacheConfiguration.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(
StringRedisSerializer()
)
)
return RedisCacheManager.builder(redisConnectionFactory!!).cacheDefaults(redisCacheConfiguration).build()
}
}
再跑,再运行:
ohhhhhhhhhhhhhhhh
Kotlin + Springboot,神吗?未必...不好用吗?也未必。
只能说SpringBoot by Kotlin,任重道远!