八 SpringBoot 整合 Redis
(一) 简单使用(存在序列化问题)
① 创建 Springboot项目或模块,引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
可以看到,这个官方的 starter 引入了 Redis,但是并没有引入 Jedis,而是引入了 Lettuce
Jedis:采用的直连,多个线程操作的话,是不安全的。如果要避免不安全,使用jedis pool连接池!更像BIO模式
Lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式
② 编写配置文件
# 配置redis
spring.redis.host=192.168.122.1
spring.redis.port=6379
③ 测试代码
@SpringBootTest
class Redis02BootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("name","zhangsan");
System.out.println(redisTemplate.opsForValue().get("name"));
}
}
运行结果:
zhangsan
(二) 使用自定义 RedisTemplate 模板(推荐)
上述操作,在 IDEA 中的结果肯定是没问题的,但是我们去 Linux 中去看一下 Redis 的内容,却发现 key 都是乱码,例如存储的 name 却变成了如下内容
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\x04name"
这就是序列化的问题,下面我们会分析这个问题,这里先给出解决方案,即自定义 RedisTemplate 模板
① 自定义 RedisConfig 类
@Configuration
public class RedisConfig {
/**
* 自定义 RedisTemplate 怒ban
*
* @param factory
* @return
*/
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 为开发方便,一般直接使用 <String, Object>
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
// Json序列化配置
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);
// String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
② 调用
@SpringBootTest
class Redis02BootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
redisTemplate.opsForValue().set("address","beijing");
System.out.println(redisTemplate.opsForValue().get("address"));
}
}
我们分别存储了 name2 和 address 这两个key,去终端中查看一下
127.0.0.1:6379> keys *
1) "address"
2) "\xac\xed\x00\x05t\x00\x04name"
3) "name2"
可以看到,问题被解决了
(三) 封装一个工具类
我们操作追求的是便捷,而每次都是用一大堆 redisTemplate.opsForValue().xxxxx 很长的命令,所以封装一个工具类能更加事半功倍,具体工具类我就不在这里贴了,因为太长了,后面我会传到 github上去然后更新链接,当然了百度上其实一搜一大把
例如下面,我们使用工具类后就可以很简单的使用封装 redisTemplate 后的方法了
@Autowired
private RedisUtil redisUtil;
@Test
void contextLoads() {
redisUtil.set("address2", "zhuhai");
System.out.println(redisUtil.get("address2"));
}
(四) 简单分析原理
这一块的简单分析,主要想要弄清楚四个内容
- ① 是不是不再使用 Jedis 了,而是使用 Lettuce
- ② 查看配置文件的配置属性
- ③ 如何操作 Redis
- ④ 序列化问题(即存储乱码问题)
在以前 Springboot 的文章中,关于自动配置的原理中可知,整合一个内容的时候,都会有一个自动配置类,然后再 spring.factories 中能找到它的完全限定类名
进入 spring.factories,查找关于 redis 的自动装配类
进入 RedisAutoConfiguration 类后,在注解中就能看到 RedisProperties 类的存在,这很显然是关于配置文件的类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
// 这个注解!!!
@EnableConfigurationProperties(RedisProperties.class)
@Import({
LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
// ...... 省略
}
进入 RedisProperties 类
alt + 7 可以在 IDEA 中查看到这个类中的属性方法等,这里查看一下其属性
例如地址端口,超时时间等等配置都清楚了,例如我们当时的配置文件是这样配的
# 配置redis
spring.redis.host=192.168.122.1
spring.redis.port=6379
继续回到类,查看下面一个注解,发现有两个类
-
LettuceConnectionConfiguration
-
JedisConnectionConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
// 这个注解!!!
@Import({
LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
// ...... 省略
}
先来进入 Jedis 这个看看,GenericObjectPool 和 Jedis 这两个内容都是默认缺失的,所以是不会生效的
再来看看 LettuceConnectionConfiguration,是没有问题的,所以确实现在默认的实现都是使用 Lettuce 了
继续回到 RedisAutoConfiguration
其 Bean 只有两个
- RedisTemplate
- StringRedisTemplate
这种 XxxTemplate ,例如 JdbcTemplate、RestTemplate 等等都是通过 Template 来操作这些组件,所以这里的两个 Template 也是这也昂,分别用来操作 Redis 和 Redis 的 String 数据类型(因为 String 类型很常用)
// 注解略
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
特别注意这一个注解
@ConditionalOnMissingBean(name = "redisTemplate")
它的意思就是说如果没有自定义,就默认使用这个,这也就是告诉我们,我们可以自己自定义Template,来覆盖掉默认的,前面的使用中我们知道,使用默认的 Template 会涉及到乱码,也就是序列化问题
因为在网络中传输的对象需要序列化,否则就是乱码
我们进入默认的 RedisTemplate 看看
首先看到的就是一些关于序列化的参数
往下看,可以看到默认的序列化方式使用的是 Jdk 序列化,而我们自定义中使用的是 Json 序列化
而默认的RedisTemplate中的所有序列化器都是使用这个序列化器
RedisSerializer 中为我们提供了多种序列化方式
所以后来我们就自定了 RedisTemplate 模板,重新定义了各种类型的序列化方式,这也是我们推荐的做法