Spring Boot集成Redis的坑,踩了!

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Spring Boot集成Redis的坑,踩了!

最近项目中使用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.1
    port: 6379
    database: 1
    password: 123456
    timeout: 5000

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

@SpringBootTest
@RunWith(SpringRunner.class)
public class TokenTest {
    @Autowired
    private RedisTemplate redisTemplate;
    @Test
    public 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
    @ConditionalOnMissingBean
    public 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注解。同时,当然少不了对源码、注解等的使用的深入学习和了解。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
存储 缓存 NoSQL
深入理解Django与Redis的集成实践
深入理解Django与Redis的集成实践
54 0
|
5天前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
17 2
|
7天前
|
NoSQL Java API
springboot项目Redis统计在线用户
通过本文的介绍,您可以在Spring Boot项目中使用Redis实现在线用户统计。通过合理配置Redis和实现用户登录、注销及统计逻辑,您可以高效地管理在线用户。希望本文的详细解释和代码示例能帮助您在实际项目中成功应用这一技术。
15 3
|
9天前
|
消息中间件 NoSQL Java
Spring Boot整合Redis
通过Spring Boot整合Redis,可以显著提升应用的性能和响应速度。在本文中,我们详细介绍了如何配置和使用Redis,包括基本的CRUD操作和具有过期时间的值设置方法。希望本文能帮助你在实际项目中高效地整合和使用Redis。
24 1
|
1月前
|
SQL JSON Java
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
这篇文章介绍了如何在Spring Boot项目中整合MyBatis和PageHelper进行分页操作,并且集成Swagger2来生成API文档,同时定义了统一的数据返回格式和请求模块。
53 1
mybatis使用三:springboot整合mybatis,使用PageHelper 进行分页操作,并整合swagger2。使用正规的开发模式:定义统一的数据返回格式和请求模块
|
28天前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
69 2
|
1月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
28 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
14天前
|
JavaScript NoSQL Java
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
CC-ADMIN后台简介一个基于 Spring Boot 2.1.3 、SpringBootMybatis plus、JWT、Shiro、Redis、Vue quasar 的前后端分离的后台管理系统
29 0
|
存储 SQL 消息中间件
springboot整合redis
redis是一个支持key-value的数据库,数据全部在内存中处理,在在一定时间间隔中将数据固化到磁盘。因为是内存操作,所以速度特别快。(这里我们主要介绍redis作为缓存使用)
207 0
springboot整合redis
|
存储 缓存 NoSQL
SpringBoot整合Redis
SpringBoot整合Redis
389 0
SpringBoot整合Redis