SpringBoot整合Redis
在 2021年这样一个人工智能飞速普及,航天飞船上天 资本家嚷嚷着要定居火星的时间段里。
我们 Java后端的日常开发也大多与 SpringBoot牢牢的绑在了一起,Redis也不例外,那我们来看看 SpringBoot如何整合 Redis。
准备工作
首先是依赖的问题,在创建项目时我们需要在 NoSQL那一栏勾选 Redis,其他的可以看心情;
勾选 Redis后,项目创建完毕我们在 pom.xml文件中可以看到 Redis的启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
复制代码
点进这个启动器拉到最底部会发现 底层已经被替换成了 lettuce,而非 Jedis,这是 SpringBoot 2.x版本后的改动(如果你比较恋旧也可以在 配置文件中换回 Jedis)
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
<version>6.0.2.RELEASE</version>
<scope>compile</scope>
</dependency>
复制代码
毕竟 2021年了,技术在疯狂的迭代 这种事情倒是不稀奇
那来聊聊为什么 SpringBoot采用 lettuce作为底层而非 Jedis呢?
首先,Jedis在实现上是直接连接的redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个Jedis实例增加物理连接才能解决问题
而 lettuce的连接是基于 netty的,连接实例可以在多个线程中进行共享,不存在线程不安全的情况;也避免了使用连接池,但偶尔也会有一些小毛病 具体什么毛病我还没碰到 感兴趣可以自行搜索一下
自动配置类
依赖方面没有困惑后,我们找到 Redis的自动配置类,看一下 SpringBoot为我们进行了哪些自动配置
点进自动配置类,通过该自动配置类找到我们需要的 properties配置类
通过 查看该 properties配置类,我们就能够清楚的得知 SpringBoot为我们做了哪些自动配置、如果要进行自定义配置要使用什么前缀
如上,通过阅读该类, 我们能够得出的一些比较关键的信息为
- 自定义配置使用的前缀为 "spring.redis",一点都不意外
- 默认使用的数据库是下标为 0的数据库
- 默认的主机为 localhost
- 默认的端口为 3306
- ......
也就是说 基本上我们都不需要对这些改动,如果不需要改端口且 server在本地的情况下
返回一开始的自动配置类,我们来看一看我们使用 SpringBoot整合 Redis最关键的部分redisTemplate
@Bean
// 首先是注解,这个注解的意思是 该Bean只有在 "redisTemplate"不存在的情况下才生效
// 换句话说,如果我们自己写了一个 redisTemplate类并注入到 Spring中那么该 bean就失效
@ConditionalOnMissingBean(name = "redisTemplate")
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 泛型都为 Object,也就是说我们使用该对象时大概率需要进行强转操作,当然也可以为了方便自己动手写一个顶掉该 Bean
// 没有进行序列化操作,我们需要手动进行序列化
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
复制代码
除此之外,由于 Redis中 String类型是使用最为频繁的数据类型,所以 单独还提出来了一个bean: stringRedisTemplate
@Bean
@ConditionalOnMissingBean
@ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
复制代码
进行测试
不得不说,一到 SpringBoot后 能讲的东西就变少了.....深了讲不会、浅了讲东西太少 那要怎么水文章呢 ( ̄_ ̄|||)
使用 SpringBoot来操作 Redis不会很难,只需要在测试类中,将 上面说到的 RedisTemplate进行注入即可
@SpringBootTest
class RedisSpringbootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
}
}
复制代码
注入后我们可以直接使用该实例来对 Redis进行操作,首先是最简单的 String类型的存取
@Test
void contextLoads() {
redisTemplate.opsForValue().set("user:1:name","moluu");
System.out.println(redisTemplate.opsForValue().get("user:1:name"));
}
复制代码
我们使用该实例中的 opsForValue().set
方法可以完成,String类型的存操作,这里的 Value指的就是String
除此之外其他的七种数据类型你都可以以 opsForXXX的形式进行操作
基本上只要你能记得住Redis的一些命令,换到使用 redisTemplate中也是可以通吃的;只是换了一种形式而已,其本质还是命令
运行测试类,可以发现已经取到了我们存的 key为 "user:1:name"的 String类型数据
(当然,运行时你要保证你的 Redis服务开着;通过自动配置类中的默认配置或者你自定义的配置,能够连接到你本地或或远程的 redis server;如果连都连不上何谈存取)
虽然可以进行存取,但我们使用 Redis官方提供的本地客户端进行查询操作就会发现存在一些问题
存进去的数据好像有些不对啊,这种情况就像是存进去了中文一样,前面一堆转义字符;为什么会这样呢?其实很简单 就是前面说到的序列化问题
RedisTemplate默认使用的是 JdkSerializationRedisSerializer,这种序列化方式会将 key和 value都序列化为了字节数组,这就造成了会返回一堆看不懂的东西
既然提到了序列化那就说一说往 Redis中存储实体类对象时需要注意的一些事情
首先我们新建一个实体类
@Component
public class User {
private String name;
private int age;
private int sex;
}
复制代码
此时我们在测试类中创建一个 User对象,将该对象作为 opsForValue().set()
的 value值传入
@Test
void contextLoads() {
User user = new User("moluu",18,1);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
复制代码
此时运行测试类会给我们报一个错误,这个错误的产生就是我们的实体类未进行序列化导致的
有两种解决方式,一种是让实体类继承 Serializable接口,使其序列化;另一种是通过第三方的序列化方式在 对象传入 value之前进行序列化操作
先看第一种
// 实现 Serializable接口
public class User implements Serializable {
private String name;
private int age;
private int sex;
}
复制代码
实体类实现 Serializable接口后,我们的操作就不会再报错了,而且正常的将我们的实体类对象存储到了 Redis中
除此之外我们还可以使用 Json在实体类对象传入 value之前对其进行序列化操作;这样也是 OK的
@Test
void contextLoads() throws JsonProcessingException {
User user = new User("moluu",18,1);
String jsonUser = new ObjectMapper().writeValueAsString(user);
// 将序列化后的 json对象作为 value值传入
redisTemplate.opsForValue().set("user",jsonUser);
System.out.println(redisTemplate.opsForValue().get("user"));
}
复制代码
自定义redisTemplate
前面在注解中我们有说到 默认的 RedisTemplate,只有在未注入同名Bean的时候才会生效;
而我们手动写一个 RedisTemplate然后注入到容器中就会将默认的给顶掉,自己写 RedisTemplate自然是有必要的
前面也看到,默认的 RedisTemplate两个泛型都是 Object;这样我们在使用 RedisTemaplate时会频繁的需要强转
这是没有必要的操作,我们在重写 RedisTemplate时将泛型根据我们的需求进行修改即可
除此之外序列化也是我们必须要处理的,jdk的序列化我们似乎并不需要
编写自定义 redisTemplate没什么好说的,大家可以抄作业也可以自己写一些能够满足自己需求的;以下为示例
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
// 泛型改成 String Object,方便我们的使用
RedisTemplate<String, Object> template = new RedisTemplate<>();
// Json序列化配置
// 使用 json解析对象
Jackson2JsonRedisSerializer objectJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
// 通过 ObjectMapper进行转义
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectJackson2JsonRedisSerializer.setObjectMapper(om);
// String的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用 String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的 key也采用 String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// Value的序列化方式采用 jackSon
template.setValueSerializer(objectJackson2JsonRedisSerializer);
// hash的 value序列化也采用 jackSon
template.setHashValueSerializer(objectJackson2JsonRedisSerializer);
template.setConnectionFactory(redisConnectionFactory);
template.afterPropertiesSet();
return template;
}
}
复制代码
自定义 RedisTemplate编写完毕后,我们将 实体类中继承的 Serializable接口去掉 再次进行测试:
@Test
void contextLoads() throws JsonProcessingException {
User user = new User("moluu",18,1);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
复制代码
此时因为已经序列化过了,所以测试类运行时不会报错 ;而且在本地的客户端中查看 key和它的 value不会再出现有转义字符的情况(引号不算)
到这里 SpringBoot集成 Redis也就没什么好讲的了,也没有那么高的水平讲太深;大家如果仍有疑惑可以移步其他文章