1. 引言
Spring Boot 之所以能让我们「开箱即用」,核心就在于它的自动配置机制。你只需要引入一个依赖,再写几行配置,框架就能自动帮你装配好所需的 Bean。这篇文章将带你从 @SpringBootApplication 出发,一步步拆解自动配置的底层原理,并手把手实现一个自定义 Starter。
2. @SpringBootApplication 拆解
@SpringBootApplication 是一个组合注解,它由以下三个核心注解组成:
java
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
}
2.1 @SpringBootConfiguration
它本质上是 @Configuration 的派生注解,表示当前类是一个配置类,Spring 容器会把它当作配置源来解析。
2.2 @ComponentScan
负责扫描当前包及其子包下的 @Component、@Service、@Repository 等注解,将它们注册为 Bean。注意它默认只扫描启动类所在的包,这也是为什么我们通常把启动类放在顶层包的原因。
2.3 @EnableAutoConfiguration
这是自动配置的入口,它通过 @Import(AutoConfigurationImportSelector.class) 引入了一个选择器,由后者去加载所有自动配置类。
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
3. 条件装配 @Conditional
自动配置类并不会无条件生效,而是通过一系列 @Conditional 注解来决定是否装配。常见的条件注解包括:
| 注解 | 作用 |
|---|---|
@ConditionalOnClass |
当类路径下存在指定类时才生效 |
@ConditionalOnMissingBean |
当容器中不存在指定 Bean 时才生效 |
@ConditionalOnProperty |
当配置文件中存在指定属性时才生效 |
@ConditionalOnWebApplication |
当应用是 Web 应用时才生效 |
例如,RedisAutoConfiguration 只有在类路径下存在 RedisOperations 类时才会被加载:
java
@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
}
这种「按需装配」的设计,既保证了功能的完整性,又避免了不必要的 Bean 创建。
4. spring.factories 与 AutoConfiguration.imports
4.1 早期的 spring.factories
在 Spring Boot 2.7 之前,自动配置类是通过 META-INF/spring.factories 文件来声明的:
properties
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.starter.MyAutoConfiguration
4.2 新的 AutoConfiguration.imports
从 Spring Boot 2.7 开始,官方推荐使用 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,每行一个自动配置类的全限定名:
text
com.example.starter.MyAutoConfiguration
新方式更简洁,也避免了 spring.factories 中大量无关配置的干扰。AutoConfigurationImportSelector 会读取这些文件,并经过条件判断后,将符合条件的配置类导入容器。
5. 自定义 Starter 示例
下面我们来实现一个简单的「问候语 Starter」,让使用者只需引入依赖即可获得一个 GreetingService。
5.1 创建自动配置类
java
package com.example.greeting;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
@AutoConfiguration
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public GreetingService greetingService(GreetingProperties properties) {
return new GreetingService(properties.getPrefix());
}
}
5.2 定义属性类
java
package com.example.greeting;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {
private String prefix = "Hello";
public String getPrefix() {
return prefix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
}
5.3 声明自动配置类
在 src/main/resources/META-INF/spring/ 下创建文件 org.springframework.boot.autoconfigure.AutoConfiguration.imports:
text
com.example.greeting.GreetingAutoConfiguration
5.4 使用 Starter
使用方只需在 pom.xml 中引入依赖,并在配置文件中指定前缀:
yaml
greeting:
prefix: "你好"
然后在代码中注入即可:
java
@Autowired
private GreetingService greetingService;
public void sayHello() {
System.out.println(greetingService.greet("Spring Boot"));
}
6. 总结
Spring Boot 的自动配置原理可以概括为三步:
@EnableAutoConfiguration通过AutoConfigurationImportSelector加载自动配置类;- 自动配置类通过
@Conditional系列注解按需装配; - 自动配置类的声明从
spring.factories演进为AutoConfiguration.imports。
理解这套机制后,你不仅能更好地排查配置问题,还能像 Spring 官方一样,写出属于自己的 Starter。