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,都能做到心中有数 。

相关推荐
無限進步D1 小时前
Java 运行原理
java·开发语言·入门
難釋懷1 小时前
安装Canal
java
是苏浙1 小时前
JDK17新增特性
java·开发语言
不光头强2 小时前
spring cloud知识总结
后端·spring·spring cloud
GetcharZp5 小时前
告别 Python 依赖!用 LangChainGo 打造高性能大模型应用,Go 程序员必看!
后端
阿里加多5 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood5 小时前
java中`==`和`.equals()`区别
java·开发语言·python
小小李程序员5 小时前
Langchain4j工具调用获取不到ThreadLocal
java·后端·ai