破若依Springboot3连接Redisson

若依 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.hostspring.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.1localhost

spring.redis.port 对应 Redis 服务器的端口号,默认是 6379,注意这里用 String 类型接收而不是 int,是为了后面字符串拼接地址时更方便,避免类型转换。

spring.redis.password 对应 Redis 的连接密码。这个字段在无密码情况下会是空字符串,后面需要做判断处理(当前代码没有处理,下文会讲到需要优化的地方)。

为什么用 spring.redis.* 而不是 spring.data.redis.*

Spring Boot 3.x 官方推荐写法是 spring.data.redis.*,但若依框架本身的代码(包括 RedisConfigRedisCache 等)读取的是 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.ymlpassword: 为空,@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 用于分布式锁等高级场景,两者各司其职,和平共存。

相关推荐
敲代码的嘎仔4 天前
Java后端面试——SSM框架面试题
java·面试·职场和发展·mybatis·ssm·springboot·八股
洛阳泰山5 天前
MaxKB4j Docker Compose 部署指南
java·docker·llm·springboot·rag·maxkb4j
l软件定制开发工作室5 天前
Spring开发系列教程(34)——打包Spring Boot应用
java·spring boot·后端·spring·springboot
我是TT啊5 天前
GaussDB_DWS连接池问题排查记录
springboot·gaussdb·druid
洛阳泰山7 天前
开源智能体搭建平台MaxKB4j 技术文档
java·开源·llm·springboot·agent·rag·langchain4j
生产队队长8 天前
SpringBoot3:应用程序启动时,初始化工作[官方文档]
springboot
闻哥8 天前
MySQL索引核心原理:B+树生成、页分裂与页合并全解析
java·jvm·b树·mysql·adb·面试·springboot
@yanyu66611 天前
第一个前后端分离项目
java·vue.js·springboot