Spring Boot 3.5 | Java 17+ | Lettuce 连接池 | Redis Cluster 读写分离 | 零配置多集群

在 Spring Boot 生态中,写一个 Starter 不难,难的是把一个 Starter 写出"层次感"。

你可能遇到过这样的场景:一个应用要同时连接多个 Redis 实例——订单缓存用 6380,用户会话用 6381,分布式锁走 Cluster 集群。官方 spring-boot-starter-data-redis 只支持单实例,社区方案要么侵入性太强,要么配置繁琐。

multi-redis-spring-boot-starter 就是为解决这个问题而生的。但今天我不只想聊"怎么连多个 Redis",更想借这个项目,和你深入聊聊 Spring Boot Starter 的两种实现模式——以及它们背后分别用到的 Spring 高级扩展点。


🎯 为什么需要两种模式?

痛点一:标准场景下的"配置疲劳"

80% 的多 Redis 需求其实很简单:连上去、能读写、序列化别出问题。但如果每次都要写 @Configuration + @Bean + 手动创建 LettuceConnectionFactory + 手动设置 RedisSerializer……这不是开发,这是复制粘贴。

痛点二:进阶场景下的"灵活性不足"

剩下 20% 的场景需要精细控制:条件化创建、自定义序列化管道、运行时动态切换集群。如果 Starter 只提供一种"黑盒"式的自动注册,开发者就会被绑死。

痛点三:Starter 实现者的"模式选择困难"

FactoryBeanImportBeanDefinitionRegistrarAutoConfigurationImportFilterSpringBootCondition——Spring 提供了太多扩展点,到底该怎么选?

multi-redis-spring-boot-starter 的答案是:不选,全都要。 只不过,每种模式各取所需。


🏛️ 架构全景

┌─────────────────────────────────────────────────────────────────┐
│                    application.yml                                │
│  spring.data.redis.clusters.order.host=localhost                │
│  spring.data.redis.clusters.cache.cluster.nodes=...             │
│  spring.data.redis.host=localhost  (官方格式兼容)                  │
└──────────────────────────┬──────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│              MultiRedisAutoConfiguration                         │
│                                                                  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  Auto-register 模式                                       │  │
│  │                                                           │  │
│  │  @Import(MultiRedisRegistrar.class)                       │  │
│  │  ┌─────────────────────────────────────────────────────┐  │  │
│  │  │ ImportBeanDefinitionRegistrar                       │  │  │
│  │  │ → FactoryBean 动态注册 RedisTemplate / StringRedis  │  │  │
│  │  └─────────────────────────────────────────────────────┘  │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                  │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │  Builder 模式                                             │  │
│  │                                                           │  │
│  │  @Bean RedisTemplateBuilder  (ConditionalOnAutoRegister   │  │
│  │                                  Disabled)                │  │
│  └───────────────────────────────────────────────────────────┘  │
│                                                                  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  MultiRedisAutoConfigurationImportFilter                  │  │
│  │  自动排除官方 RedisAutoConfiguration                       │  │
│  └──────────────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────────────┘
                           │
                           ▼
┌──────────────────────────────────────────────────────────────────┐
│                    Lettuce Connection Layer                       │
│  ┌──────────┐  ┌──────────┐  ┌──────────────┐  ┌────────────┐  │
│  │ order    │  │ user     │  │ cache        │  │ session    │  │
│  │ :6380    │  │ :6381    │  │ Cluster      │  │ Cluster    │  │
│  │ Standalone│  │ Standalone│ │ 7001-7003   │  │ 7011-7013  │  │
│  └──────────┘  └──────────┘  └──────────────┘  └────────────┘  │
└──────────────────────────────────────────────────────────────────┘

核心设计理念

  1. Auto-register 模式——零代码,写 YAML 就能用,底层用 ImportBeanDefinitionRegistrar + FactoryBean 动态注册 Bean
  2. Builder 模式——代码控制,注入 RedisTemplateBuilder 手动构建,通过 @Bean 方法精细控制每个 RedisTemplate 的创建

两种模式互斥,通过自定义 SpringBootCondition 自动判断,也可手动切换。


🚀 模式一:Auto-register——写 YAML,直接注入

✨ 核心理念:零代码,开箱即用

Auto-register 模式的哲学很简单:开发者只需要告诉 Starter “连哪些 Redis”,剩下的事 Starter 全包。

📝 配置即一切

spring:
  data:
    redis:
      clusters:
        order:                           # Standalone → orderRedisTemplate
          host: localhost
          port: 6380
          serializer:
            key: string
            value: json
        user:                            # Standalone → userRedisTemplate
          host: localhost
          port: 6381
        cache:                           # Redis Cluster → cacheRedisTemplate
          cluster:
            nodes: localhost:7001,localhost:7002,localhost:7003
          lettuce:
            cluster:
              refresh:
                adaptive: true
                period: 2000ms

没有 @Configuration,没有 @Bean,没有任何 Java 代码。

🪄 注入即用

@Service
public class OrderService {

    private final RedisTemplate<String, Object> orderRedisTemplate;
    private final StringRedisTemplate userStringRedisTemplate;

    public OrderService(RedisTemplate<String, Object> orderRedisTemplate,
                        StringRedisTemplate userStringRedisTemplate) {
        this.orderRedisTemplate = orderRedisTemplate;
        this.userStringRedisTemplate = userStringRedisTemplate;
    }
}

Bean 名称遵循 {clusterName}RedisTemplate / {clusterName}StringRedisTemplate 命名约定,Spring 按参数名自动匹配注入。

🔧 背后用了哪些 Spring 扩展点?

Auto-register 模式的实现,是本项目最"硬核"的部分,它涉及 4 个 Spring 高级扩展点的协同:

1. ImportBeanDefinitionRegistrar——动态注册 Bean 定义
public class MultiRedisRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
                                        BeanDefinitionRegistry registry) {
        // 1. 从 Environment 解析所有 spring.data.redis.clusters.* 配置
        Map<String, ClusterConfig> clusters = resolveClusters();

        // 2. 为每个 cluster 注册 3 个 BeanDefinition
        for (Map.Entry<String, ClusterConfig> entry : clusters.entrySet()) {
            String clusterName = entry.getKey();
            ClusterConfig config = entry.getValue();

            // LettuceConnectionFactory
            GenericBeanDefinition factoryDef = new GenericBeanDefinition();
            factoryDef.setBeanClass(LettuceConnectionFactory.class);
            factoryDef.setInstanceSupplier(() -> createConnectionFactory(config));
            registry.registerBeanDefinition(clusterName + "RedisConnectionFactory", factoryDef);

            // RedisTemplate (via FactoryBean)
            GenericBeanDefinition templateDef = new GenericBeanDefinition();
            templateDef.setBeanClass(RedisTemplateFactoryBean.class);
            templateDef.getConstructorArgumentValues().addGenericArgumentValue(factoryBeanName);
            templateDef.getConstructorArgumentValues().addGenericArgumentValue(config);
            registry.registerBeanDefinition(clusterName + "RedisTemplate", templateDef);

            // StringRedisTemplate (via FactoryBean)
            // ...同理注册
        }
    }
}

这意味着:Bean 的数量和名称完全由 YAML 配置决定,Starter 在启动时动态生成,不需要开发者手写一个 @Bean

2. FactoryBean——延迟创建,精确控制实例化
public class RedisTemplateFactoryBean implements FactoryBean<RedisTemplate<String, Object>>, BeanFactoryAware {

    @Override
    public RedisTemplate<String, Object> getObject() {
        LettuceConnectionFactory factory = beanFactory.getBean(connectionFactoryBeanName, LettuceConnectionFactory.class);
        return MultiRedisRegistrar.createRedisTemplate(factory, config);
    }

    @Override
    public Class<?> getObjectType() {
        return RedisTemplate.class;
    }
}

为什么用 FactoryBean 而不是直接 setInstanceSupplier?因为 FactoryBean 提供了延迟创建能力——只有当容器真正需要这个 Bean 时,getObject() 才会被调用。同时,通过 BeanFactoryAware 回调拿到 BeanFactory,可以在运行时按名称查找对应的 LettuceConnectionFactory,实现 Factory → Template 的松耦合装配

3. AutoConfigurationImportFilter——自动排除官方配置
public class MultiRedisAutoConfigurationImportFilter implements AutoConfigurationImportFilter {

    private static final Set<String> EXCLUDED_CLASSES = Set.of(
        "org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration",
        "org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration",
        "org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration"
    );

    @Override
    public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata metadata) {
        boolean[] match = new boolean[autoConfigurationClasses.length];
        for (int i = 0; i < autoConfigurationClasses.length; i++) {
            match[i] = autoConfigurationClasses[i] == null || !EXCLUDED_CLASSES.contains(autoConfigurationClasses[i]);
        }
        return match;
    }
}

这意味着:引入 multi-redis-starter 后,官方单实例 Redis 自动配置被静默排除,用户不需要在 @SpringBootApplication(exclude = {...}) 里手动排除。

4. 官方配置格式零侵入兼容

更贴心的是,Auto-register 模式还兼容了 Spring Boot 官方的 Standalone 和 Cluster 两种配置格式:

# 官方 Standalone 格式 → defaultRedisTemplate
spring:
  data:
    redis:
      host: localhost
      port: 6379

# 官方 Cluster 格式 → defaultRedisTemplate
spring:
  data:
    redis:
      cluster:
        nodes: localhost:7001,localhost:7002,localhost:7003

从官方 starter 迁移过来,不需要改任何配置,只需替换依赖。MultiRedisRegistrar 会检测到官方格式配置并自动归入名为 default 的集群——无论是 spring.data.redis.host 还是 spring.data.redis.cluster.nodes,都能无缝接管。


🎨 模式二:Builder——注入 Builder,想怎么建怎么建

✨ 核心理念:代码控制,灵活定制

当你需要以下条件时,Builder 模式是你的菜:

  • 自定义序列化器(比如用特定的 JSON 库)
  • 条件化创建(@ConditionalOnProperty
  • 运行时动态决定连哪个集群
  • 自定义 pipeline、事务管理器

🪄 一行代码,一个集群

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> orderRedisTemplate(RedisTemplateBuilder builder) {
        // 自定义序列化器
        return builder.cluster("order")
                .keySerializer(RedisSerializer.string())
                .valueSerializer(RedisSerializer.json())
                .hashKeySerializer(RedisSerializer.string())
                .hashValueSerializer(RedisSerializer.json())
                .build();
    }

    @Bean
    public RedisTemplate<String, Object> userRedisTemplate(RedisTemplateBuilder builder) {
        // 使用 YAML 配置的序列化器(零覆盖)
        return builder.cluster("user").build();
    }

    @Bean
    public StringRedisTemplate orderStringRedisTemplate(RedisTemplateBuilder builder) {
        return builder.stringTemplate("order");
    }
}

RedisTemplateBuilder 的设计灵感来自 Spring AI 的 ChatClient.Builder 模式——注入一个 Builder,通过链式 API 构建目标对象。

🔧 RedisTemplateConfigurer——Fluent API 的优雅

public class RedisTemplateConfigurer<K, V> {

    private final RedisTemplate<K, V> template;

    public RedisTemplateConfigurer<K, V> keySerializer(RedisSerializer<?> serializer) {
        template.setKeySerializer(serializer);
        return this;
    }

    public RedisTemplateConfigurer<K, V> valueSerializer(RedisSerializer<?> serializer) {
        template.setValueSerializer(serializer);
        return this;
    }

    // ... hashKeySerializer, hashValueSerializer, serializers(全部统一)

    public RedisTemplate<K, V> build() {
        template.afterPropertiesSet();
        return template;
    }
}

builder.cluster("order") 返回 RedisTemplateConfigurer,所有序列化器方法返回 this,最终 .build() 完成初始化。这是经典的 Builder + Fluent Interface 组合模式。


🔄 模式切换:自定义 SpringBootCondition 的妙用

两种模式互斥,如何自动判断?答案是自定义 SpringBootCondition

public class OnAutoRegisterDisabledCondition extends SpringBootCondition {

    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String autoRegisterProp = context.getEnvironment().getProperty("spring.data.redis.auto-register");

        if (autoRegisterProp != null) {
            // 用户显式设置了属性,尊重用户选择
            boolean autoRegister = Boolean.parseBoolean(autoRegisterProp);
            return autoRegister
                ? ConditionOutcome.noMatch("auto-register is explicitly enabled")
                : ConditionOutcome.match("auto-register is explicitly disabled");
        }

        // 未设置属性,自动检测 Redis 配置
        boolean hasConfig = hasOfficialFormatConfig(context) || hasMultiClusterConfig(context);
        return hasConfig
            ? ConditionOutcome.noMatch("Redis configuration detected, auto-register will be auto-enabled")
            : ConditionOutcome.match("No Redis configuration detected, Builder mode is active");
    }
}

配合自定义注解 @ConditionalOnAutoRegisterDisabled,实现模式自动切换:

场景 配置 激活模式
多集群格式 spring.data.redis.clusters.order.host=... Auto-register
官方 Standalone spring.data.redis.host=127.0.0.1 Auto-register
官方 Cluster spring.data.redis.cluster.nodes=... Auto-register
显式开启 spring.data.redis.auto-register: true Auto-register
显式关闭 spring.data.redis.auto-register: false Builder

优先级:显式 auto-register 设置 > 自动检测


📊 两种模式对比速查

维度 Auto-register 模式 Builder 模式
定位 零代码,开箱即用 代码控制,灵活定制
配置方式 纯 YAML Java @Configuration + @Bean
序列化器 YAML 声明式(string/json/java/byteArray 代码式(RedisSerializer.xxx() 或自定义)
Bean 命名 自动:{name}RedisTemplate 手动:@Bean 方法名
核心扩展点 ImportBeanDefinitionRegistrar + FactoryBean RedisTemplateBuilder + RedisTemplateConfigurer
适用场景 标准多集群读写 自定义序列化、条件化创建、动态路由
推广话术 写 YAML,直接注入 注入 Builder,想怎么建怎么建

📊 涉及的 Spring 扩展点速查

扩展点 实现类 职责
ImportBeanDefinitionRegistrar MultiRedisRegistrar 根据 YAML 动态注册 Bean 定义
FactoryBean RedisTemplateFactoryBean / StringRedisTemplateFactoryBean 延迟创建 RedisTemplate 实例
AutoConfigurationImportFilter MultiRedisAutoConfigurationImportFilter 自动排除官方 Redis 自动配置
SpringBootCondition OnAutoRegisterDisabledCondition 自定义条件,决定模式切换
@AutoConfiguration MultiRedisAutoConfiguration Starter 入口,编排所有组件

这 5 个扩展点,几乎覆盖了 Spring Boot Starter 开发中最常用的高级模式。如果你正在学习"如何写一个高质量的 Starter",这个项目的源码值得逐行阅读。


🎓 学习价值

对于初中级开发者

  • ✅ 理解 Spring Boot Starter 的"约定优于配置"理念
  • ✅ 学会用 YAML 配置驱动 Bean 注册(Auto-register 模式)
  • ✅ 掌握构造器注入多 Bean 的命名约定技巧

对于高级开发者

  • ✅ 深入理解 ImportBeanDefinitionRegistrar 的动态 Bean 注册机制
  • ✅ 掌握 FactoryBean 的延迟创建与 BeanFactoryAware 的松耦合装配
  • ✅ 学习 Builder + Fluent Interface 的 API 设计范式

对于架构师

  • ✅ 双模式互斥设计:SpringBootCondition + 自定义条件注解
  • AutoConfigurationImportFilter 实现无感排除官方配置
  • ✅ 官方配置格式零侵入兼容的迁移策略
  • ✅ Builder + Fluent Interface 的 API 设计范式

🚦 快速开始

前置要求

  • Java 17+
  • Spring Boot 3.5.x
  • 至少一个 Redis 实例(Standalone 或 Cluster)

3 步启动

1. 引入依赖

<dependency>
    <groupId>org.hongxi</groupId>
    <artifactId>multi-redis-spring-boot-starter</artifactId>
    <version>1.0.4</version>
</dependency>

2. 写配置

spring:
  data:
    redis:
      clusters:
        order:
          host: localhost
          port: 6380
          serializer:
            key: string
            value: json
        cache:
          cluster:
            nodes: localhost:7001,localhost:7002,localhost:7003

3. 注入使用

@RestController
public class DemoController {

    private final RedisTemplate<String, Object> orderRedisTemplate;
    private final StringRedisTemplate cacheStringRedisTemplate;

    public DemoController(RedisTemplate<String, Object> orderRedisTemplate,
                          StringRedisTemplate cacheStringRedisTemplate) {
        this.orderRedisTemplate = orderRedisTemplate;
        this.cacheStringRedisTemplate = cacheStringRedisTemplate;
    }

    @GetMapping("/order/{key}")
    public Object getOrder(@PathVariable String key) {
        return orderRedisTemplate.opsForValue().get(key);
    }
}

就这么简单——没有 @Configuration,没有手动创建 ConnectionFactory,没有 afterPropertiesSet()


💡 最佳实践总结

1. 优先使用 Auto-register 模式

除非你需要自定义序列化器或条件化创建,否则 Auto-register 模式是最佳选择。YAML 声明式配置更直观、更易维护。

2. 序列化器配置推荐

serializer:
  key: string        # Key 统一用 String,便于在 redis-cli 中查看
  value: json        # Value 用 JSON,跨语言兼容、可读性好
  hash-key: string
  hash-value: json

3. Redis Cluster 开启拓扑自动刷新

cache:
  cluster:
    nodes: localhost:7001,localhost:7002,localhost:7003
  lettuce:
    cluster:
      refresh:
        adaptive: true      # 自适应刷新:拓扑变更时立即触发
        period: 2000ms      # 周期性刷新:兜底保障

这意味着:即使集群发生主从切换或节点扩缩容,客户端也能自动感知新拓扑,无需重启应用。

4. Builder 模式中复用 YAML 序列化配置

@Bean
public RedisTemplate<String, Object> userRedisTemplate(RedisTemplateBuilder builder) {
    // 不覆盖序列化器 → 自动使用 YAML 中配置的 serializer
    return builder.cluster("user").build();
}

@Bean
public RedisTemplate<String, Object> orderRedisTemplate(RedisTemplateBuilder builder) {
    // 覆盖序列化器 → 使用代码中指定的
    return builder.cluster("order")
            .valueSerializer(new MyCustomSerializer())
            .build();
}

这意味着:YAML 配置是"默认值",代码覆盖是"特例",两者可以共存。


🔗 相关链接


📝 结语

回到开头的问题:一个 Starter 到底可以有几种"写法"?

multi-redis-spring-boot-starter 用两种模式给出了答案——Auto-register 模式用 ImportBeanDefinitionRegistrar + FactoryBean 实现零配置动态注册;Builder 模式用 RedisTemplateBuilder + RedisTemplateConfigurer 链式 API 实现代码级精细控制。两种模式通过 SpringBootCondition 自动切换,通过 AutoConfigurationImportFilter 无感排除官方配置。

一个 Starter,5 个 Spring 高级扩展点,两种截然不同的设计哲学——"约定优于配置"与"代码优于配置"在这里不是二选一,而是各取所长。

如果你正在

  • 🎯 在项目中接入多个 Redis 实例/集群
  • 🚀 学习 Spring Boot Starter 的高级实现模式
  • 🏗️ 设计自己的 Starter 并寻找架构参考

Star ⭐ multi-redis-spring-boot-starter,一起探索 Starter 的更多可能!

git clone https://github.com/javahongxi/multi-redis-spring-boot-starter.git

© hongxi.org

Logo

一站式 AI 云服务平台

更多推荐