自动配置原理与自定义自动配置(深入浅出 + 实战 starter 示例)
开篇语
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛
今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。
我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。
小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!
1) 自动配置的基本原理(启动时发生了什么)
-
Spring Boot 在启动时会从类路径下查找 “自动配置候选类” 列表(历史上是
META-INF/spring.factories,新版本推荐/使用META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports),把这些配置类逐个加载并判断条件(如@ConditionalOnClass、@ConditionalOnMissingBean等),满足条件的配置类会被应用到 Spring 上下文,进而注册 Bean。 -
关键点:所谓“自动配置”本质就是一系列带条件的
@Configuration(或标注为@AutoConfiguration)类,这些配置类不是靠组件扫描发现的,而是通过 自动配置注册表(上面那个META-INF文件)告诉 Spring Boot:“启动时请考虑这些配置”。只有当配置类上所有条件成立时,才会真正注入 Bean。
2) 常用条件注解解析(带“我会怎么用”的建议)
下面列出最常见也最实用的几种条件注解,并解释常见误区。
@ConditionalOnClass
- 含义:只有当指定类在 classpath 上可见时,才匹配通过(也可用类名字符串避免类加载问题)。这通常用于“如果应用引入了某个可选依赖,则启用相关自动配置”。
- 使用建议:当你的自动配置依赖某个可选库(例如
spring-web、spring-redis、第三方 SDK),用@ConditionalOnClass(SomeClass.class)把配置包裹起来,避免在缺少该依赖时出现ClassNotFoundException。 - 常见坑:把
@ConditionalOnClass写在@Bean方法上且用返回值类型做匹配,可能会因为类加载顺序导致判断问题;通常把条件放在配置类级别更稳妥。
@ConditionalOnMissingBean
- 含义:当 IoC 容器中不存在某类/名称/注解匹配的 Bean 时,条件成立(也就是——只有没有用户定义的同类型 Bean 时,才注入默认 Bean)。
- 使用建议:这是实现“可覆盖默认实现”的首选注解。你的 starter 提供默认 Bean,但应用可以通过显式声明同接口/同类型 Bean 来覆盖它。
- 常见坑:声明
@ConditionalOnMissingBean时要注意类型匹配(接口 vs 实现类 vs 返回类型),有时因为泛型或候选类型不一致导致条件失配,从而出现“默认 Bean 没注入 / 无法覆盖”的问题。
其它有用条件(列举并简短说明)
@ConditionalOnProperty:基于配置属性启/停(例如my.starter.enabled=true)。非常适合给 starter 增加开关。@ConditionalOnBean:要求容器已有某 Bean(用于“只有在存在某体系时才启用”)。@ConditionalOnMissingClass、@ConditionalOnExpression等:做更细化条件判断。
(关于条件注解的实现、语义与边界,官方有完整说明,可参考 docs)。
3) 自动配置加载顺序与优先级
自动配置类之间的顺序对最终结果可能影响很大(尤其当两个 auto-config 都能创建同类型 Bean 时)。Spring Boot 提供了注解来控制顺序:
-
@AutoConfigureBefore/@AutoConfigureAfter:声明某个自动配置类应当在另一个之前或之后应用(通常写类名或类全名字符串)。适用于你需要在别的自动配置之前/之后生效的场景(例如 web 相关配置)。 -
@AutoConfigureOrder:直接指定顺序值(不常用,更多是配合@AutoConfiguration时使用)。
原则:尽量写“局部且稳妥”的依赖顺序(before/after)而不是全局靠 order 值乱设,因为值大小难以全局协调。
4) 自定义自动配置模块(构建 starter)——实战(完整代码示例)
下面我们一步步做一个最小可用的 starter:greeting-starter。它的目的很简单:提供一个 GreetingService 的默认实现,并暴露 greeting.message 配置项。应用若引入该 starter,会自动得到默认 GreetingService;如果应用自己声明一个 GreetingService,则默认实现会被覆盖。
说明:示例用 Java + Maven。示例兼容 Spring Boot 2.x / 3.x 的基本做法;注意
META-INF注册方式在新旧版本上有差别(见注册节)。官方 auto-config 指南是权威参考。
4.1 项目骨架(maven 坐标 & 结构)
greeting-starter/
src/main/java/com/example/greeting/ # 代码
GreetingService.java
DefaultGreetingService.java
GreetingProperties.java
GreetingAutoConfiguration.java
src/main/resources/META-INF/
spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
pom.xml
pom.xml(核心片段):
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>greeting-starter</artifactId>
<version>0.1.0</version>
<packaging>jar</packaging>
<dependencies>
<!-- 只依赖 spring-boot-autoconfigure & spring-boot-configuration-processor(optional) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
备注:
spring-boot-configuration-processor用于在 IDE 中自动生成@ConfigurationProperties的元数据,方便开发者补全和验证。
4.2 定义接口与默认实现
GreetingService.java:
package com.example.greeting;
public interface GreetingService {
String greet(String name);
}
DefaultGreetingService.java:
package com.example.greeting;
public class DefaultGreetingService implements GreetingService {
private final String message;
public DefaultGreetingService(String message) {
this.message = message;
}
@Override
public String greet(String name) {
return String.format("%s, %s!", message, name);
}
}
4.3 配置属性类(@ConfigurationProperties)
GreetingProperties.java:
package com.example.greeting;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {
/**
* 是否启用 starter 提供的功能(默认 true)
*/
private boolean enabled = true;
/**
* 默认问候语
*/
private String message = "Hello";
// getters & setters
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}
4.4 自动配置类(关键:@AutoConfiguration + 条件注解)
GreetingAutoConfiguration.java:
package com.example.greeting;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
@EnableConfigurationProperties(GreetingProperties.class)
@ConditionalOnProperty(prefix = "greeting", name = "enabled", havingValue = "true", matchIfMissing = true)
public class GreetingAutoConfiguration {
private final GreetingProperties properties;
public GreetingAutoConfiguration(GreetingProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public GreetingService greetingService() {
return new DefaultGreetingService(properties.getMessage());
}
}
解释与要点:
@AutoConfiguration:Spring Boot 推荐用于自动配置类的注解(等价/改进于以前的@Configuration+ 桩)。自动配置类通过META-INF/...AutoConfiguration.imports被发现并考虑。@EnableConfigurationProperties(GreetingProperties.class):确保GreetingProperties被绑定并注入到配置类构造函数中。@ConditionalOnProperty(prefix="greeting", name="enabled", havingValue="true", matchIfMissing=true):提供一个开关,方便使用方通过greeting.enabled=false来禁用 starter。@ConditionalOnMissingBean:若应用已提供GreetingService的 Bean,则不会注入默认实现,从而实现覆盖能力。
4.5 在 META-INF 注册(新旧注册文件)
新方式(Spring Boot 推荐,Spring Boot 3.x+):
创建文件:
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
内容:
com.example.greeting.GreetingAutoConfiguration
旧方式(历史兼容):
META-INF/spring.factories
# spring.factories 里的一行示例(旧)
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.greeting.GreetingAutoConfiguration
小结:较新 Spring Boot 版本倾向于使用
...AutoConfiguration.imports文件来声明自动配置候选类,社区/官方也在逐步迁移这个机制(参考为何从spring.factories迁移的讨论)。如果你的 starter 要兼容多版本 Boot,可同时提供两者(注意维护)。
4.6 使用方(应用如何引入并覆盖)
应用 pom.xml 添加依赖:
<dependency>
<groupId>com.example</groupId>
<artifactId>greeting-starter</artifactId>
<version>0.1.0</version>
</dependency>
application.yml(启用自定义配置):
greeting:
enabled: true
message: "Hi from my starter"
覆盖默认实现(在应用中自定义 Bean):
@Bean
public GreetingService myGreetingService() {
return name -> "Yo " + name; // 将覆盖 starter 的 DefaultGreetingService
}
注入使用:
@RestController
public class HelloController {
private final GreetingService greetingService;
public HelloController(GreetingService greetingService) {
this.greetingService = greetingService;
}
@GetMapping("/hello")
public String hello(@RequestParam(defaultValue = "World") String name) {
return greetingService.greet(name);
}
}
4.7 测试自动配置(简单集成测试示例)
测试 1:当依赖存在且未覆盖时,bean 应存在
@SpringBootTest(properties = {"greeting.enabled=true"})
public class GreetingAutoConfigTest {
@Autowired
ApplicationContext context;
@Test
void greetingServicePresent() {
assertTrue(context.containsBean("greetingService"));
GreetingService s = context.getBean(GreetingService.class);
assertEquals("Hello, Alice!", s.greet("Alice")); // 如果 properties.message 默认 Hello
}
}
测试 2:当应用自定义 Bean 时,自动配置的 Bean 不生效
@SpringBootTest(classes = {GreetingAutoConfigTest.CustomConfig.class})
public class GreetingAutoConfigOverrideTest {
@Configuration
static class CustomConfig {
@Bean
public GreetingService greetingService() {
return name -> "Custom " + name;
}
}
@Autowired
GreetingService greetingService;
@Test
void userBeanOverrides() {
assertEquals("Custom Bob", greetingService.greet("Bob"));
}
}
5) 常见陷阱与调试技巧(为什么我的自动配置“没生效”?)
-
没把自动配置类写入
META-INF注册文件:最常见的错误。没有注册的自动配置永远不会被考虑(除非被组件扫描捕获,但那不是自动配置机制的正确用法)。 -
条件注解写错了或放错位置:把
@ConditionalOnClass放在@Bean方法上而不是配置类上,或对类型指定得不够精确,可能导致判断失败或类加载异常。优先在类级别写条件更稳定。 -
忘记
@EnableConfigurationProperties/ properties 未绑定:结果是GreetingProperties无法注入或值为默认值,导致行为与预期不符。确保在自动配置类上启用 properties。 -
与其他 auto-config 的顺序冲突:如果你的 auto-config 需要在 web auto-config 之后才能正确工作,记得用
@AutoConfigureAfter(WebMvcAutoConfiguration.class)或对应的before/after指定顺序。 -
组件扫描误用:不要通过组件扫描来实现自动配置(例如把自动配置类放在应用可能会扫描到的包下),正确做法是通过
META-INF/...AutoConfiguration.imports注册。否则会出现“在某些环境下生效、在另一些环境下不生效”的不稳定行为。 -
版本兼容与注册文件差异:不同 Spring Boot 版本在自动配置注册机制上有差别(
spring.factories→AutoConfiguration.imports),要注意兼容策略或明确你的目标 Boot 版本。
6) 小结与建议(何时写 starter,如何发布/维护)
-
什么时候写 starter? 当你有一组复用组件(SDK、公共库、业务中台功能)需要在多个应用中一致配置与装配时,写 starter 能显著降低接入成本与配置重复。
-
如何维护好 starter?
- 明确语义化配置项(prefix + 详尽注释),并提供
application.yml示例。 - 提供配置开关(
enabled),使使用者能快速禁用。 - 测试覆盖:提供自动化测试,覆盖“未引入依赖”“用户覆盖 Bean”“属性启/停”等场景。
- 发布时注意 Spring Boot 兼容性(如果可能,给出多个版本分支或说明兼容的 Boot 版本)。
- 明确语义化配置项(prefix + 详尽注释),并提供
-
调试技巧:启动应用时用
--debug或查看ConditionEvaluationReport,Spring Boot 会输出条件匹配信息,能帮助你定位哪个条件没通过(为什么某个 auto-config 被跳过)。官方文档有相关说明。
参考资料(权威来源,值得收藏)
- Spring Boot 官方:Creating your own auto-configuration / Developing auto-configuration(自动配置开发指南)。
@ConditionalOnClassAPI 说明(官方 javadoc)。@ConditionalOnMissingBeanAPI 说明(官方 javadoc)。- 关于 spring.factories → AutoConfiguration.imports 的讨论(StackOverflow / issue),用于理解注册机制演进。
- Auto-configuration 顺序讨论(StackOverflow / 官方 - before/after 注解)。
结尾(诚恳建议与“别犯的错”清单)
如果你现在只是想验证思路:先做一个本地的 greeting-starter,把 jar 安装到本地仓库(mvn install),再在一个 demo 项目里引入依赖、测试启用/禁用/覆盖。
如果你要发布到公司内部或公共仓库:记得写好 README(包含配置项、示例、兼容性说明)、加上 spring-boot-configuration-processor 的注解处理器以生成元数据、并尽量提供 spring.factories + AutoConfiguration.imports 双注册以兼容不同 Boot 版本(如果你需要支持老版本)。
… …
文末
好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。
… …
学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!
wished for you successed !!!
⭐️若喜欢我,就请关注我叭。
⭐️若对您有用,就请点赞叭。
⭐️若有疑问,就请评论留言告诉我叭。
版权声明:本文由作者原创,转载请注明出处,谢谢支持!
- 点赞
- 收藏
- 关注作者
评论(0)