专栏名称: Moluuu
待业
目录
相关文章推荐
营养师顾中一  ·  含「硅油」洗发水不脱发,但有人确实不太适合 ·  3 天前  
营养师顾中一  ·  能做好这个动作,未来7年死亡风险大大降低! ·  4 天前  
丁香生活研究所  ·  哇塞,这牛排也太绝了吧!(价格打下来了) ·  1 周前  
营养师顾中一  ·  红薯、紫薯、白薯,营养更好的是? ·  6 天前  
51好读  ›  专栏  ›  Moluuu

【Redis】SpringBoot整合Redis

Moluuu  · 掘金  ·  · 2021-02-24 21:33

正文

阅读 51

【Redis】SpringBoot整合Redis

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也就没什么好讲的了,也没有那么高的水平讲太深;大家如果仍有疑惑可以移步其他文章


放松一下眼睛

原图P站地址

画师主页