若依 Spring Boot 3 集成 Redisson 完整教程
一、前言与背景
在现代企业级 Java 开发中,Redis 已经成为不可或缺的中间件,广泛用于缓存、会话管理、分布式锁、消息队列等场景。若依(RuoYi)作为国内最流行的 Spring Boot 快速开发框架之一,默认使用 Spring Data Redis + Lettuce 客户端来操作 Redis。
然而在实际项目开发中,我们经常需要用到分布式锁 、分布式集合 、限流 等高级功能,这些功能用原生 Spring Data Redis 实现起来非常繁琐。而 Redisson 作为一个功能强大的 Redis Java 客户端,在 Spring Data Redis 的基础上提供了大量开箱即用的分布式工具,因此很多开发者会选择在若依项目中引入 Redisson。
但问题在于:若依原版使用的是 spring.redis.* 配置项,而直接引入 redisson-spring-boot-starter 后,Redisson 会尝试接管整个 Redis 连接工厂,导致各种冲突和报错。本教程将详细讲解如何在不破坏若依原有 Redis 机制的前提下,正确地手动集成 Redisson,使两者和平共存。
二、为什么不直接用 redisson-spring-boot-starter
在开始之前,必须搞清楚一个核心问题:为什么不直接引入 redisson-spring-boot-starter 就完事了?
redisson-spring-boot-starter 是 Redisson 官方提供的 Spring Boot 自动配置包,它做的事情是:自动读取 Redis 配置,创建 RedissonClient,并且自动将 RedisConnectionFactory 替换为 RedissonConnectionFactory。
这在纯 Redisson 项目中没有问题,但在若依中会引发严重冲突,原因如下:
第一 ,若依的 RedisConfig 类中手动定义了 RedisTemplate 的 Bean,它期望注入的是 Lettuce 提供的 LettuceConnectionFactory,而不是 Redisson 的 RedissonConnectionFactory,两者接口虽然相同但行为有差异,导致 Bean 注入失败。
第二 ,redisson-spring-boot-starter 在解析配置时,会尝试读取 spring.redis.host、spring.redis.password 等属性,但若依 Spring Boot 3 版本的配置有时写的是 spring.data.redis.*,导致占位符无法解析,直接报 Could not resolve placeholder 错误。
第三 ,redisson-spring-boot-starter 引入了大量自动配置类,与若依已有的配置类产生 Bean 重复定义冲突,启动时报 There is already a bean named xxx 之类的错误。
正确的做法 是:只引入 Redisson 的核心库 redisson,不引入 starter,然后手动编写一个 RedissonConfig 配置类 ,自己控制 RedissonClient 的创建过程,完全绕开自动配置的干扰。这也正是本教程所采用的方案。
三、环境准备
在开始之前,确认你的开发环境满足以下条件:
- JDK 17 或以上
- Spring Boot 3.x
- 若依 Spring Boot 版本(非微服务版)
- Redis 服务已安装并正常运行(本地
127.0.0.1:6379,无密码) - Maven 构建工具
- IDEA 开发环境
四、第一步:引入正确的 Maven 依赖
打开项目根目录或 ruoyi-common 模块的 pom.xml,添加以下依赖:
xml
<!-- Redisson 核心库,注意不是 redisson-spring-boot-starter -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.27.2</version>
</dependency>
特别注意以下几点:
第一,版本必须选择支持 Spring Boot 3 的版本,Redisson 3.21.0 以上均可与 Spring Boot 3 配合使用,推荐使用 3.27.x 最新稳定版。
第二,绝对不能 同时引入 redisson-spring-boot-starter,如果你之前引入过,必须先删掉:
xml
<!-- 删掉这个,否则会冲突 -->
<!--
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>x.x.x</version>
</dependency>
-->
第三,若依项目是多模块结构,建议将 Redisson 依赖加在 ruoyi-common 模块,因为这个模块是所有其他模块的公共依赖,这样整个项目都能使用 RedissonClient。
添加完依赖后,点击 IDEA 右侧 Maven 面板的刷新按钮,等待依赖下载完成。
五、第二步:配置 application.yml
打开 ruoyi-admin/src/main/resources/application.yml,确保 Redis 配置使用的是 spring.redis.* 而不是 spring.data.redis.*,因为我们的 RedissonConfig 类用 @Value 注解读取的正是 spring.redis.* 路径:
yaml
spring:
redis:
# Redis服务器地址
host: 127.0.0.1
# Redis服务器端口
port: 6379
# Redis数据库索引(默认为0)
database: 0
# Redis服务器密码,没有密码留空即可,不要填null或任何字符
password:
# 连接超时时间
timeout: 10s
lettuce:
pool:
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大连接数
max-active: 8
# 连接池最大阻塞等待时间(负值表示没有限制)
max-wait: -1ms
关于 password 字段的注意事项:
这是若依 Redisson 集成中最容易踩坑的地方。password: 后面什么都不写表示空字符串,这是正确的无密码配置。千万不要写成 password: null(这会让 Spring 读到字符串 "null"),也不要写成 password: "" 加引号(某些版本会有问题)。
如果你的 Redis 有密码,比如密码是 mypassword123,则写:
yaml
password: mypassword123
六、第三步:编写 RedissonConfig 配置类
这是整个教程的核心。在 ruoyi-framework 模块的 config 包下,创建 RedissonConfig.java 文件,完整代码如下:
java
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Redisson配置类
* 手动创建 RedissonClient,避免与若依原有 Redis 配置冲突
*
* @author ruoyi
*/
@Configuration
public class RedissonConfig
{
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redissonClient()
{
Config config = new Config();
config.useSingleServer()
.setAddress("redis://" + host + ":" + port);
return Redisson.create(config);
}
}
七、逐行深度解析 RedissonConfig
7.1 @Configuration 注解
java
@Configuration
public class RedissonConfig
@Configuration 是 Spring 的核心注解之一,它告诉 Spring 容器:这个类是一个配置类,类中用 @Bean 注解标注的方法返回的对象会被注册为 Spring Bean,可以被其他类通过 @Autowired 注入使用。
与普通的 @Component 相比,@Configuration 标注的类中的 @Bean 方法调用会被 Spring 代理拦截,确保同一个 Bean 只被创建一次(单例),这对于 RedissonClient 这种重量级的连接客户端来说非常重要,绝对不能创建多个实例。
7.2 @Value 注解读取配置
java
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
@Value 注解用于从 Spring 的 Environment 中读取配置值,${} 是占位符语法,Spring 启动时会自动将其替换为 application.yml 中对应的值。
这里读取了三个配置项:
spring.redis.host 对应 Redis 服务器的 IP 地址,本地开发一般是 127.0.0.1 或 localhost。
spring.redis.port 对应 Redis 服务器的端口号,默认是 6379,注意这里用 String 类型接收而不是 int,是为了后面字符串拼接地址时更方便,避免类型转换。
spring.redis.password 对应 Redis 的连接密码。这个字段在无密码情况下会是空字符串,后面需要做判断处理(当前代码没有处理,下文会讲到需要优化的地方)。
为什么用 spring.redis.* 而不是 spring.data.redis.*?
Spring Boot 3.x 官方推荐写法是 spring.data.redis.*,但若依框架本身的代码(包括 RedisConfig、RedisCache 等)读取的是 spring.redis.* 路径,为了与框架保持一致,这里统一使用 spring.redis.*。
7.3 @Bean 注解
java
@Bean(destroyMethod = "shutdown")
@Bean 将这个方法的返回值注册为 Spring 容器中的一个 Bean。
destroyMethod = "shutdown" 这个参数非常重要。它告诉 Spring:当应用程序关闭、Spring 容器销毁时,自动调用 RedissonClient 对象的 shutdown() 方法。这样可以优雅地关闭 Redisson 的所有连接池、线程池,释放网络资源,避免资源泄露和连接池未关闭导致的应用hang住问题。如果不配置这个参数,应用关闭时 Redisson 的线程可能无法正常退出。
7.4 @ConditionalOnMissingBean 注解
java
@ConditionalOnMissingBean(RedissonClient.class)
这个注解的含义是:只有当 Spring 容器中还没有 RedissonClient 类型的 Bean 时,才创建这个 Bean。
这是一种防御性编程的思路。在大型项目中,可能有多个模块都定义了 RedissonClient,加上这个注解后,即使其他地方也定义了 RedissonClient,也不会产生 Bean 重复定义的冲突,Spring 会使用先注册的那个,忽略后面的。这让代码更健壮,也更容易被其他开发者覆盖扩展。
7.5 Config 和 useSingleServer
java
Config config = new Config();
config.useSingleServer()
.setAddress("redis://" + host + ":" + port);
return Redisson.create(config);
Config 是 Redisson 的核心配置对象,它支持多种 Redis 部署模式:
useSingleServer() 对应单机模式,适合本地开发和单节点 Redis 部署,返回一个 SingleServerConfig 对象,可以链式调用各种配置方法。
useClusterServers() 对应集群模式,适合 Redis Cluster 生产环境。
useSentinelServers() 对应哨兵模式,适合主从 + 哨兵的高可用部署。
useMasterSlaveServers() 对应主从模式。
setAddress("redis://" + host + ":" + port) 设置 Redis 服务器地址,注意地址格式必须是 redis://ip:port,如果是 SSL 连接则使用 rediss://ip:port。
最后 Redisson.create(config) 根据配置创建并返回 RedissonClient 实例。
八、密码处理的优化
当前代码有一个潜在问题:没有处理密码为空的情况。如果 application.yml 中 password: 为空,@Value 注入后 password 是空字符串 "",如果直接调用 setPassword(""),某些版本的 Redisson 仍然会尝试发送 AUTH 命令,导致 Redis 报错。
建议将 redissonClient() 方法优化为:
java
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(RedissonClient.class)
public RedissonClient redissonClient()
{
Config config = new Config();
org.redisson.config.SingleServerConfig serverConfig =
config.useSingleServer()
.setAddress("redis://" + host + ":" + port)
.setDatabase(0)
.setConnectionPoolSize(32)
.setConnectionMinimumIdleSize(8)
.setConnectTimeout(10000)
.setTimeout(3000)
.setRetryAttempts(3)
.setRetryInterval(1500);
// 只有密码非空时才设置密码,避免发送多余的 AUTH 命令
if (password != null && !password.trim().isEmpty())
{
serverConfig.setPassword(password);
}
return Redisson.create(config);
}
这样无论 Redis 有没有设密码,都能正确处理。
九、如何在业务代码中使用 RedissonClient
配置完成后,在任意 Spring 管理的 Bean 中通过 @Autowired 或构造器注入即可使用:
java
@Service
public class OrderService
{
@Autowired
private RedissonClient redissonClient;
/**
* 使用分布式锁防止重复下单
*/
public void createOrder(String userId)
{
// 获取分布式锁,锁的key与业务相关
RLock lock = redissonClient.getLock("order:lock:" + userId);
try
{
// 尝试加锁,等待时间5秒,锁自动释放时间30秒
boolean locked = lock.tryLock(5, 30, TimeUnit.SECONDS);
if (!locked)
{
throw new RuntimeException("操作太频繁,请稍后再试");
}
// 执行业务逻辑
doCreateOrder(userId);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
finally
{
// 只有当前线程持有锁才释放,防止误释放
if (lock.isHeldByCurrentThread())
{
lock.unlock();
}
}
}
}
十、常见错误与解决方案汇总
错误一:Could not resolve placeholder 'spring.redis.host'
原因是 application.yml 里写的是 spring.data.redis.host,但代码读的是 spring.redis.host。解决方法是将配置改为 spring.redis.host。
错误二:ERR AUTH called without any password configured
原因是 Redis 没有密码,但代码传了空字符串密码,Redis 拒绝了 AUTH 命令。解决方法是按照第八节的优化代码,加上密码非空判断。
错误三:Unable to connect to Redis server: localhost/127.0.0.1:6379
原因是 Redis 服务没有启动,或者防火墙阻止了连接。解决方法是先用 redis-cli ping 确认 Redis 正常运行。
错误四:Bean 重复定义冲突
原因是同时引入了 redisson-spring-boot-starter 和手动配置类。解决方法是删掉 redisson-spring-boot-starter 依赖,只保留 redisson 核心库。
十一、总结
本教程的核心思路可以概括为三点:
第一,只引入 redisson 核心库 ,不引入 redisson-spring-boot-starter,避免自动配置与若依框架产生冲突。
第二,手动编写 RedissonConfig ,通过 @Value 读取已有的 spring.redis.* 配置,自己控制 RedissonClient 的创建,与若依原有的 Lettuce + RedisTemplate 体系完全隔离、互不干扰。
第三,注意密码为空的处理,这是若依项目中最常见的踩坑点,通过非空判断可以完美解决。
按照本教程配置完成后,若依项目就同时拥有了两套 Redis 操作方式:原有的 RedisCache(基于 RedisTemplate)用于日常缓存操作,新引入的 RedissonClient 用于分布式锁等高级场景,两者各司其职,和平共存。