spring-boot 整合redis
技术版本说明
1. springboot .version - 2.2.2.RELEASE
2. jdk .version - 8
3. lombok.version - version - 1.18.10
4. hutool-all.version - 5.6.2
5. spring-boot-starter-data-redis。version - 延用springboot里的版本(即 2.2.2.RELEASE)
6. fastjson - version - 1.2.75
源码地址
gitee地址: https://gitee.com/zjydzyjs/spring-boot-use-case-collection/tree/master/spring-boot-no-sql/redis
yml配置说明
如下图(yml配置):
备注: 我这里只是简单配置,如果需要自定义复杂配置,请自行配置;
想知道都支持那些属性配置的?
- 请搜索类 {@link RedisProperties.class};
package: org.springframework.boot.autoconfigure.data.redis
下的
RedisProperties.class
直接通过点击yml配置中的jedis里的最后一层内容即可跳转到具体类,例如点击application-dev.yml 中的max-idle 属性;
通过找寻Redis自动配置类来找到具体的属性配置;
右边 RedisAutoConfiguration 点击进去,即可找到
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
// 省略
......
}
@EnableConfigurationProperties 可以得知Redis配置类是RedisProperties.class
不进行序列化配置和序列化配置后进行测试
spring-boot-starter-data-redis 封装了对象用于对redis的操作,你可以理解为这是spring-boot帮我们写好了一个用于操作redis的工具类(或服务类),这个类叫
RedisTemplate.class
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
private boolean enableTransactionSupport = false;
private boolean exposeConnection = false;
private boolean initialized = false;
private boolean enableDefaultSerializer = true;
@Nullable
private RedisSerializer<?> defaultSerializer;
@Nullable
private ClassLoader classLoader;
@Nullable
private RedisSerializer keySerializer = null;
@Nullable
private RedisSerializer valueSerializer = null;
@Nullable
private RedisSerializer hashKeySerializer = null;
@Nullable
private RedisSerializer hashValueSerializer = null;
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
@Nullable
private ScriptExecutor<K> scriptExecutor;
private final ValueOperations<K, V> valueOps = new DefaultValueOperations(this);
private final ListOperations<K, V> listOps = new DefaultListOperations(this);
private final SetOperations<K, V> setOps = new DefaultSetOperations(this);
private final StreamOperations<K, ?, ?> streamOps = new DefaultStreamOperations(this, new ObjectHashMapper());
private final ZSetOperations<K, V> zSetOps = new DefaultZSetOperations(this);
private final GeoOperations<K, V> geoOps = new DefaultGeoOperations(this);
private final HyperLogLogOperations<K, V> hllOps = new DefaultHyperLogLogOperations(this);
private final ClusterOperations<K, V> clusterOps = new DefaultClusterOperations(this);
public RedisTemplate() {
}
// 省略
}
StringRedisTemplate
spring-boot 其实还封装了一个StringRedisTemplate.class 类,这个类是专门用于操作Key 、Value 都是String类型的,
public class StringRedisTemplate extends RedisTemplate<String, String> {
public StringRedisTemplate() {
this.setKeySerializer(RedisSerializer.string());
this.setValueSerializer(RedisSerializer.string());
this.setHashKeySerializer(RedisSerializer.string());
this.setHashValueSerializer(RedisSerializer.string());
}
public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
this.setConnectionFactory(connectionFactory);
this.afterPropertiesSet();
}
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}
从StringRedisTemplate类中我们发现其实它对如下属性进行了设置:
private RedisSerializer keySerializer = null; // 对Key序列化的RedisSerializer private RedisSerializer valueSerializer = null; //对Value序列化的RedisSerializer private RedisSerializer hashKeySerializer = null;
// 对HashKey序列化的RedisSerializer
private RedisSerializer hashValueSerializer = null;
// 对hashValue序列化的RedisSerializer
为什么要设置呢?
从 this.afterPropertiesSet(); 中可以发现 在RedisTemplate.class中
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (this.defaultSerializer == null) {
// 当没有指定默认的序列化器时,默认使用JdkSerializationRedisSerializer 序列化器 (这里会出现很多的问题,后面写用例的时候我再说)
this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader());
}
// 默认为true,所以key、value、hashKey、hashValue 都会使用自己设置的,如果没有设置的话就会使用this.defaultSerializer
if (this.enableDefaultSerializer) {
if (this.keySerializer == null) {
this.keySerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.valueSerializer == null) {
this.valueSerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.hashKeySerializer == null) {
this.hashKeySerializer = this.defaultSerializer;
defaultUsed = true;
}
if (this.hashValueSerializer == null) {
this.hashValueSerializer = this.defaultSerializer;
defaultUsed = true;
}
}
if (this.enableDefaultSerializer && defaultUsed) {
Assert.notNull(this.defaultSerializer, "default serializer null and not all serializers initialized");
}
if (this.scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor(this);
}
this.initialized = true;
}
测试用例
String类型测试
// set String类型
@Test
void contextLoads() {
setStr();
}
void setStr(){
String keyStr = "testKey";
redisTemplate.opsForValue().set(keyStr,"testVal");
System.out.println("str:"+redisTemplate.opsForValue().get(keyStr));
}
结果:
图中可以发现该key已经存在了,但是会存在很难直接理解的字符,那么这个是因为什么呢?
问题
其实,出现这个的原因就是因为我们现在使用的是RedisTemplate.class ,你可以打开你的这个类查看 afterPropertiesSet() 方法,也可以查看我上面写的StringRedisTemplate中描述的 "为什么要设置呢?" 那里,就可以知道,为什么会变成这样子,因为它使用了 JdkSerializationRedisSerializer 所以导致该String类型的数据,key和value都无法直接理解。
解决
- 使用StringRedisTemplate
- 自己封装一个RedisTemplate 用来注入到spring中
效果
StringRedisTemplate 效果
MyRedisConfig
// 增加MyRedisConfig配置
@Configuration
public class MyRedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
// 使用jackson 进行序列化
this.jackson(template);
//this.fastJson(template);
template.afterPropertiesSet();
return template;
}
private void jackson(RedisTemplate<String, Object> template){
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
}
}
自定义Object类型类型测试
这里使用 T.class;
注意:测试的时候请先将MyRedisConfig注释,不然不会出现异常信息!
@Data
@AllArgsConstructor
public class T {
private String name;
private Integer index;
private String value;
}
void setObj(){
String keyObj = "obj";
redisTemplate.opsForValue().set(keyObj,new T("testObj",1,"testVal"));
System.out.println("obj:"+redisTemplate.opsForValue().get(keyObj));
问题
运行后出现异常:
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.blacktea.redis.dto.T]
怎么解决?
因为要使用Jdk序列化,所以T.class必须的实现接口 Serializable
@Data
@AllArgsConstructor
public class T implements Serializable {
private static final long serialVersionUID = 1875172526779913430L;
private String name;
private Integer index;
private String value;
}
效果
这样还是会存在String类型测试那里的问题,可以使用 MyRedisConfig.class 自己配置解决,也可以手动把key、value自己转换成String。
手动转换,例如:
redisTemplate.opsForValue().set(keyObj, JSONUtil.toJsonStr(obj));
测试总结
你可以将key、value转换成String类型即可是实现在redis显示可以直观理解的内容。
将key、value 转化,可以通过实现 RedisSerializer 接口来自定义序列化处理。
可以参考:
FastJsonRedisSerializer.class
Jackson2JsonRedisSerializer.class
StringRedisSerializer.class