在 Spring Boot 的世界里,“约定优于配置” 理念通过自动配置机制展现得淋漓尽致。从一个简单的@SpringBootApplication注解开始,背后隐藏着一套精妙的自动配置加载流程。本文将从@SpringBootApplication出发,逐步拆解自动配置类是如何被发现、加载到 Spring 容器中的,结合关键源码,带你看透自动配置的本质。
一、@SpringBootApplication:自动配置的入口
注解拆解
@SpringBootApplication是一个复合注解,核心包含以下三个关键注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 标记为Spring Boot配置类,本质是@Configuration
@EnableAutoConfiguration // 开启自动配置核心注解
@ComponentScan(excludeFilters = { // 组件扫描,默认扫描当前包及子包@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {//... 其他属性
}
其中,@EnableAutoConfiguration是触发自动配置的关键开关,它的功能实现依赖 @Import 导入的ImportSelector实现类,开启自动配置类的加载流程。
关于@Import注解的详细内容,可以看:
Spring 中 @Import 注解:Bean 注入的灵活利器-CSDN博客
二、@EnableAutoConfiguration:开启自动配置引擎
@EnableAutoConfiguration 的结构
@EnableAutoConfiguration注解定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";Class<?>[] exclude() default {};String[] excludeName() default {};
}
这里通过 @Import 引入 AutoConfigurationImportSelector ,这个类实现了 ImportSelector接口,负责收集并返回需要自动配置的类名集合,是自动配置类加载的核心桥梁。
三、AutoConfigurationImportSelector:筛选自动配置类
1)selectImports 方法:启动加载流程
AutoConfigurationImportSelector 实现了 ImportSelector接口,重写 selectImports方法:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) { return NO_IMPORTS;}AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
逻辑是:先判断自动配置是否启用(可通过spring.boot.enableautoconfiguration
属性控制),若启用则调用getAutoConfigurationEntry
获取自动配置入口,最终返回需要注入容器的配置类名数组。
2)getAutoConfigurationEntry:构建配置入口
getAutoConfigurationEntry方法是加载自动配置类的核心逻辑入口:
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 1. 获取注解属性AnnotationAttributes attributes = getAttributes(annotationMetadata); // 2. 获取候选配置类List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 3. 去重、排除处理// 去重configurations = removeDuplicates(configurations); // 排除指定配置Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 4. 条件过滤configurations = getConfigurationClassFilter().filter(configurations);// 5. 事件发布fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions);
}
1. 获取注解属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
通过 getAttributes 方法,从 @EnableAutoConfiguration 注解元数据(annotationMetadata)里,提取注解属性(像 exclude、excludeName 等配置 ),为后续筛选配置类提供依据,明确哪些配置类要排除。
2. 获取候选配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
getCandidateConfigurations 方法是核心,它会去加载 META-INF/spring.factories 文件中,key 为 EnableAutoConfiguration 对应的配置类全限定名,把这些配置类收集成列表,作为自动配置的 “候选池” 。
3. 去重、排除处理
去重:
configurations = removeDuplicates(configurations);
调用 removeDuplicates,遍历 configurations 列表,剔除重复的配置类全限定名,保证配置类唯一。
排除指定配置
Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions);
4. 条件过滤
- getExclusions:从注解属性里解析出用户通过 exclude 等指定的、要排除的配置类名,存入 exclusions 集合。
- checkExcludedClasses:校验排除的类是否合法(比如类是否存在等 )。
- configurations.removeAll(exclusions):从候选配置类列表里,移除 exclusions 中指定的配置类,实现用户自定义排除。
configurations = getConfigurationClassFilter().filter(configurations);
getConfigurationClassFilter 获取条件过滤器(内部结合 @Conditional 系列注解,如 @ConditionalOnClass、@ConditionalOnBean 等逻辑 ),对 configurations 列表遍历筛选,只有满足所有 @Conditional 条件的配置类,才会保留,确保最终加载的配置类符合运行环境要求。
5. 事件发布
fireAutoConfigurationImportEvents(configurations, exclusions);
fireAutoConfigurationImportEvents 方法发布自动配置加载事件(如AutoConfigurationImportEvent ),让项目里的 ApplicationListener 监听器能感知到自动配置加载过程,便于做拓展(比如记录日志、额外初始化逻辑等 )。
3)getCandidateConfigurations:加载 spring.factories 配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "+ "are using a custom packaging, make sure that file is correct.");return configurations;
}@Override
protected Class<?> getSpringFactoriesLoaderFactoryClass() {return EnableAutoConfiguration.class;
}
关键逻辑:
SpringFactoriesLoader.loadFactoryNames 会从所有 Jar 包的META-INF/spring.factories文件中,读取key为 EnableAutoConfiguration.class 对应的配置类列表。例如,Spring Boot 自身的自动配置、第三方 Starter(如 MyBatis - Starter)的自动配置,都通过这种方式 “注册” 到加载流程中。
四、SpringFactoriesLoader:实现配置类发现
SpringFactoriesLoader是 Spring 框架提供的工具类,核心方法loadFactoryNames逻辑简化如下:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); Map<String, List<String>> factories = new LinkedHashMap<>();while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {factories.add(factoryTypeName, factoryImplementationName.trim()); }}}cache.put(classLoader, factories);return factories;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}
}
其中:
- FACTORIES_RESOURCE_LOCATION 常量值为"META-INF/spring.factories",指定了配置文件路径。
- 方法会遍历类路径下所有 Jar 包的META-INF/spring.factories文件,将文件内容解析为Properties,再根据factoryType(这里是EnableAutoConfiguration)的全类名,提取对应的配置类名列表。
五、自动配置类的生效:@Conditional 的把关
加载到候选配置类后,Spring Boot 并非直接全部注入容器,而是通过 **@Conditional系列注解 ** 做 “条件校验”。常见条件注解:
- @ConditionalOnClass:类路径下存在指定类时生效。
- @ConditionalOnBean:容器中存在指定 Bean 时生效。
- @ConditionalOnProperty:配置属性满足条件时生效(如spring.datasource.enabled=true )。
以DataSourceAutoConfiguration为例,简化代码:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "javax.sql.DataSource")
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {//... 配置数据源Bean逻辑
}
只有项目依赖了DataSource相关类、容器中没有自定义DataSource Bean 时,该自动配置类才会真正生效,向容器注入数据源相关 Bean。
六、总结:自动配置全流程脉络
从@SpringBootApplication开始,自动配置的完整流程可梳理为:
1. 注解触发:@SpringBootApplication 包含 @EnableAutoConfiguration,通过 @Import引入AutoConfigurationImportSelector(ImportSelector接口的实现类)。
2. 收集配置类:AutoConfigurationImportSelector 的 selectImports 方法调用getAutoConfigurationEntry,再通过 getCandidateConfigurations,借助SpringFactoriesLoader 读取所有 Jar 包META-INF/spring.factories 中EnableAutoConfiguration 对应的配置类。
3. 筛选生效类:结合 @Conditional 注解,过滤出满足条件的自动配置类,注入 Spring 容器,完成自动配置。
这套机制让 Spring Boot 能根据项目依赖 “智能” 配置环境,既降低了手动配置的复杂度,又通过@Conditional保证了灵活性,是 Spring Boot “开箱即用” 特性的核心支撑。理解这一流程,无论排查自动配置问题,还是自定义 Starter,都能做到心中有数 。