
本文介绍如何通过模板方法模式对多个仅配置标识符不同的 spring boot `@configuration` 类进行统一抽象,减少代码重复,提升可维护性。
在 Spring Boot 项目中,当存在多个功能结构一致、仅依赖不同命名前缀(如 "configA" / "configB")的配置类时,直接复制粘贴会导致显著的代码冗余和维护风险。理想的重构方式是提取共性逻辑,将差异点参数化——模板方法模式(Template Method Pattern) 正是解决此类问题的经典设计模式。
该模式将算法骨架定义在抽象父类中,而将可变步骤延迟到子类实现。在 Spring 配置场景下,我们可定义一个抽象基类(不加 @Configuration),由具体子类继承并注入差异化 Bean,同时保留 @Bean 的声明能力与 Spring 容器集成。
以下是推荐的重构实践:
✅ 正确实现方式(推荐)
// 抽象基类:定义配置骨架,不标注 @Configuration
public abstract class BaseConfig {
// 模板方法:可在此封装通用初始化逻辑(非必须)
protected void onConfigInitialized() {
// 例如:日志记录、健康检查注册等
}
// 声明抽象钩子方法,由子类提供具体 Bean 实例及名称
protected abstract First firstBean();
protected abstract Second secondBean();
}// 具体子类:标注 @Configuration,复用模板逻辑
@Configuration
public class ConfigA extends BaseConfig {
private static final String PREFIX = "configA";
@Override
@Bean(name = "first" + PREFIX)
public First firstBean() {
return new First("instance-for-" + PREFIX); // 示例构造逻辑
}
@Override
@Bean(name = "second" + PREFIX)
public Second secondBean() {
return new Second("instance-for-" + PREFIX);
}
}@Configuration
public class ConfigB extends BaseConfig {
private static final String PREFIX = "configB";
@Override
@Bean(name = "first" + PREFIX)
public First firstBean() {
return new First("instance-for-" + PREFIX);
}
@Override
@Bean(name = "second" + PREFIX)
public Second secondBean() {
return new Second("instance-for-" + PREFIX);
}
}⚠️ 关键注意事项:抽象基类 BaseConfig 不可加 @Configuration,否则 Spring 会尝试实例化它(失败),且导致 Bean 名称冲突或重复注册;@Bean 注解必须保留在子类的具体方法上,Spring 仅扫描 @Configuration 类中的 @Bean 方法;若需共享初始化逻辑(如条件校验、资源预加载),可在基类中定义 protected 模板方法,并在子类 @Bean 方法中显式调用(如 onConfigInitialized());Bean 类型应保持一致(如 First/Second),确保 DI 语义清晰;若需差异化类型,可结合泛型进一步增强扩展性。
✅ 替代方案(适用于更复杂场景)
- @ConfigurationProperties + @ConditionalOnProperty:若配置差异本质是外部属性驱动,可统一用配置类绑定 + 条件化加载;
- @Import 动态导入:通过工厂类根据环境/参数返回不同 @Configuration 类,适合运行时动态决策;
- Java Config + @Bean 工厂方法:将 CONFIG_STRING 作为方法参数传入,配合 @Bean 的 name SpEL 表达式(如 name = "first#{systemProperties['config.id']}"),但需确保属性可用且命名可控。
综上,模板方法模式在保持 Spring 原生配置语义的同时,以最小侵入性实现配置类的结构复用,是应对“多配置类仅命名不同”问题的简洁、稳健且符合 Spring 最佳实践的重构路径。










