最近项目中使用SpringBoot集成Redis,踩到了一个坑:从Redis中获取数据为null,但实际上Redis中是存在对应的数据的。是什么原因导致此坑的呢?

本文就带大家从SpringBoot集成Redis、所踩的坑以及自动配置源码分析来学习一下SpringBoot中如何正确的使用Redis。

SpringBoot集成Redis

在SpringBoot项目中只需在pom文件中引入Redis对应的starter,配置Redis连接信息即可进行使用了。pom依赖引入:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

对应application配置文件配置:

spring:redis:host: 127.0.0.1port: 6379database: 1password: 123456timeout: 5000

通过以上两项配置即完成了Redis的集成,下面便是具体的使用,这里以单元测试的形式呈现。

@SpringBootTest
@RunWith(SpringRunner.class)
public class TokenTest {@Autowiredprivate RedisTemplate redisTemplate;@Testpublic void getValue() {Object value = redisTemplate.opsForValue().get("1");System.out.println("value:" + value);}
}

可以看到直接通过@Autowired注入RedisTemplate之后,即可调用RedisTemplate提供的方法操作。RedisTemplate提供了丰富的Redis操作方法,具体使用查看相应的API即可,这里不再拓展。

项目中遇到的坑

回归到最开始的问题:从Redis中获取数据为null,但实际上Redis中是存在对应的数据的。

其实问题表象很诡异,但问题的原因很简单,就是Redis中存数据和取数据时采用了不同的RedisTemplate导致的。

在SpringBoot中,针对Redis的自动配置类默认会初始化两个RedisTemplate,先来看一下RedisAutoConfiguration中源码:

@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
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@ConditionalOnMissingBeanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}
}

可以看到RedisAutoConfiguration中初始化了两个RedisTemplate的bean。第一个Bean类型为RedisTemplate<Object, Object>,Bean的名称为redisTemplate,而且是当容器中不存在对应的Bean name时才会进行初始化。第二Bean类型为StringRedisTemplate,Bean的名称为stringRedisTemplate,该类继承自RedisTemplate<String, String>。

也就说一个Bean是针对Object对象处理的,一个是针对String对象进行处理的。

导致出现坑的原因便是set时注入的是RedisTemplate<Object, Object>,而获取时注入的是StringRedisTemplate。这么明显的错误应该很容易排查的啊?

问题为什么隐藏的那么深?

如果直接是因为两处类型不一致导致的,的确很好排查,看一下注入的RedisTemplate即可。

但问题难以排查,还因为另外一个因素:@Resource和@Autowired注入的问题。

默认情况下@Resource采用先根据bean名称注入,找不到再根据类型注入,而@Autowired默认采用根据类型注入。项目获取数据时采用了@Resource注入方式,如下:

@Resource
private RedisTemplate<String, String> redisTemplate;

而存储时采用的是@Autowired注入的:

@Autowired
private RedisTemplate<String, String> redisTemplate;

上面两种形式的注入,在只存在单个实例时好像并不是什么问题,要么其中一个直接报错,要么注入成功。但当像上述场景,出现了两个RedisTemplate时,问题就变得隐蔽了。

当采用@Autowired时,根据类型注入,直接注入了RedisTemplate<String, String>的bean,因为它们的类型都是String的。

而当使用@Resource注入时,默认采用的是根据名称匹配,源码中可以看到redisTemplate对应的类型为RedisTemplate<Object, Object>。因此,两处注入了不同的RedisTemplate,于是就导致了获取时获取不到值的问题。

解决方案

找到问题的根源之后,解决问题便容易多了。

方案一,将@Resource的注入改为@Autowired。

方案二:将@Resource注入的bean名称由redisTemplate改为stringRedisTemplate。当然根据具体业务场景还有其他解决方案。

小结

关于SpringBoot集成Redis其实很简单,SpringBoot已经帮我们做了大多数的事情,但因为默认初始化了两个RedisTemplate,再加上@Autowired和@Resource注解的区别就导致了问题的复杂度。因此,在使用的过程中尽量保持各处采用一致的规范,阿里Java开发手册推荐使用@Resource注解。同时,当然少不了对源码、注解等的使用的深入学习和了解。


往期推荐

文件写入的6种方法,这种方法性能最好

线程池的7种创建方式,强烈推荐你用它...

求求你,别再用wait和notify了!

关注我,每天陪你进步一点点!

Spring Boot集成Redis,这个坑把我害惨了!相关推荐

  1. Spring Boot集成Redis缓存之模拟高并发场景处理

    前言 同样我们以上一篇文章为例子,搭建好环境之后,我欧美可以模拟高并发场景下,我们的缓存效率怎么样,到底能不能解决我们实际项目中的缓存问题.也就是如何解决缓存穿透? Spring Boot集成Redi ...

  2. Spring Boot集成Redis缓存之RedisTemplate的方式

    前言 Spring Boot 集成Redis,将自动配置 RedisTemplate,在需要使用的类中注入RedisTemplate的bean即可使用 @Autowired private Redis ...

  3. Linux 安装Redis-6.2.5,配置及使用(RDB与AOF持久化、sentinel机制、主从复制、Spring Boot 集成 Redis)

    CentOS 7 安装Redis-6.2.5版本 Redis采用的是基于内存的单进程 单线程模型 的KV数据库,由C语言编写.官方提供的数据是可以达到100000+的qps 应用场景: 令牌(Toke ...

  4. Spring Boot 集成 Redis 缓存

    Spring Boot 集成 Redis 缓存 在此章,我们将 SpringBoot 集成 Redis 缓存,Redis是一个开源的,基于内存的数据结构存储,可以用作数据库.缓存和消息代理,在本章仅讲 ...

  5. 【第二十三篇】Spring Boot集成redis

    1.1 简介 REmote DIctionary Server(Redis)是一个由Salvatore Sanfilippo写的key-value存储系统. Redis是一个开源的使用ANSI C语言 ...

  6. spring boot 集成 redis spring-boot-starter-data-redis 2.1.7.RELEASE jedis: pool: #连接池配置 及踩坑经验

    目录 先上一些踩坑报错,各类报错 @org.springframework.beans.factory.annotation.Autowired(required=true) Error creati ...

  7. Spring Boot集成Redis实现缓存

    前言 在此章,我们将 SpringBoot 集成 Redis缓存,Redis是一个开源的,基于内存的数据结构存储,可以用作数据库.缓存和消息代理,在本章仅讲解缓存集成.一键获取源码地址 准备工作 当前 ...

  8. springboot 集成redis_一文详解Spring Boot 集成 Redis

    redis设置: 修改redis服务器的配置文件 vim /usr/local/redis/bin/redis.confbind 0.0.0.0 protected-mode no 重新启动redis ...

  9. Spring Boot集成Redis缓存之注解方式

    首先还是加入依赖Jar pom.xml中加入依赖 <!-- 加载spring boot redis 包 --><dependency><groupId>org.sp ...

最新文章

  1. 职场残酷!美国科技巨头 “花式劝退” 老员工,数万人丢掉饭碗
  2. [单刷APUE系列]第五章——标准I/O库
  3. JUC原子类-数组类型(三)
  4. java –cp_Java –缺少字体–崩溃的应用程序!
  5. 元类及创建元类的方法(面试用
  6. Vim引申以及Linux下彩色进度条实现
  7. ios开发--清理缓存
  8. JAVA实现的小程序
  9. 关于一些初级ACM竞赛题目的分析和题解(九)
  10. 手机微信桌面计算机不见了怎么办,微信桌面图标不见了怎么办
  11. linux微信电脑版登录不了,默认Windows与Deepin系统下的微信电脑版目录
  12. 2000款学校教师课件培训PPT模板免费下载网址
  13. 渠道分销管理系统解决方案
  14. 如何做将两张图片合二为一
  15. 解决Java应用的后台错误:“操作符不存在: character varying = bytea“
  16. CE实战:修改植物大战僵尸中阳光数值
  17. 机器学习之---CRF与MRF
  18. 懒惰(Laziness)、急躁(Impatience)和傲慢(Hubris)
  19. python查询JCR分区
  20. 智能床垫监测体征助力智慧养老,更加智能化的智能床垫-新导智能

热门文章

  1. 用python画大雄_python制作斗图生成器
  2. 神经网络的全连接层_深度神经网络全连接层
  3. 有源光缆AOC在40G网络布线中备受欢迎的主要原因
  4. HTTP1.1新增了五种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 、 CONNECT
  5. apply()与call()的区别
  6. 车联网领域,传统TSP企业做错了什么 ?
  7. I00037 亏数(Deficient number)
  8. python实现链表的删除_Python中 为我们提供了一些独特的解决方案的方法特性
  9. 项目经理到底要不要懂技术?
  10. Linux卸载/删除多余网卡