一个 Starter,两种哲学:multi-redis-spring-boot-starter 双模式架构深度解读
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 实现者的"模式选择困难"
FactoryBean、ImportBeanDefinitionRegistrar、AutoConfigurationImportFilter、SpringBootCondition——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 │ │
│ └──────────┘ └──────────┘ └──────────────┘ └────────────┘ │
└──────────────────────────────────────────────────────────────────┘
核心设计理念:
- Auto-register 模式——零代码,写 YAML 就能用,底层用
ImportBeanDefinitionRegistrar+FactoryBean动态注册 Bean - 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 配置是"默认值",代码覆盖是"特例",两者可以共存。
🔗 相关链接
- 📦 项目地址: github.com/javahongxi/multi-redis-spring-boot-starter
- 📖 Spring Boot 官方文档: spring.io/projects/spring-boot
- 📖 Spring Data Redis: docs.spring.io/spring-data/redis
- 📖 Lettuce 客户端: lettuce.io
- 📦 Maven Central: central.sonatype.com
📝 结语
回到开头的问题:一个 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
更多推荐



所有评论(0)