Spring Boot 自动配置全流程深度解析

在 Spring Boot 的世界里,"约定优于配置" 理念通过自动配置机制展现得淋漓尽致。从一个简单的@SpringBootApplication注解开始,背后隐藏着一套精妙的自动配置加载流程。本文将从@SpringBootApplication出发,逐步拆解自动配置类是如何被发现、加载到 Spring 容器中的,结合关键源码,带你看透自动配置的本质。

一、@SpringBootApplication:自动配置的入口

注解拆解

@SpringBootApplication是一个复合注解,核心包含以下三个关键注解:

java 复制代码
@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注解定义如下:

java 复制代码
@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方法:

java 复制代码
@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方法是加载自动配置类的核心逻辑入口:

java 复制代码
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 配置

java 复制代码
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逻辑简化如下:

java 复制代码
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为例,简化代码:

java 复制代码
@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.factorie s 中EnableAutoConfiguration 对应的配置类。

3. 筛选生效类:结合 @Conditional 注解,过滤出满足条件的自动配置类,注入 Spring 容器,完成自动配置。

这套机制让 Spring Boot 能根据项目依赖 "智能" 配置环境,既降低了手动配置的复杂度,又通过@Conditional保证了灵活性,是 Spring Boot "开箱即用" 特性的核心支撑。理解这一流程,无论排查自动配置问题,还是自定义 Starter,都能做到心中有数 。

相关推荐
带刺的坐椅36 分钟前
老码农教你 Solon Web Context-Path 的两种配置方式
java·nginx·tomcat·web·solon
ZZHow10241 小时前
Java项目-苍穹外卖_Day2
java·spring boot·web
Cisyam1 小时前
使用Bright Data API轻松构建LinkedIn职位数据采集系统
后端
float_六七1 小时前
Spring Boot 3为何强制要求Java 17?
java·spring boot·后端
叫我阿柒啊1 小时前
从Java全栈到前端框架的深度探索
java·微服务·typescript·vue3·springboot·前端开发·全栈开发
bobz9651 小时前
ovs arp
后端
_風箏1 小时前
SpringBoot【集成ElasticSearch 01】2种方式的高级客户端 RestHighLevelClient 使用(依赖+配置+客户端API测试源码
后端
用户21411832636021 小时前
dify案例分享-零基础上手 Dify TTS 插件!从开发到部署免费文本转语音,测试 + 打包教程全有
后端
架构师沉默2 小时前
Java 开发者别忽略 return!这 11 种写法你写对了吗?
java·后端·架构
EndingCoder2 小时前
React 19 与 Next.js:利用最新 React 功能
前端·javascript·后端·react.js·前端框架·全栈·next.js