Skip to content

自动配置原理 ​

深入理解SpringBoot的自动配置机制,掌握@EnableAutoConfiguration的工作原理,理解自动配置类如何被加载和应用的完整流程。

📋 自动配置概览 ​

SpringBoot的自动配置是其核心特性之一,通过"约定优于配置"的思想,自动配置应用所需的Bean。

🎯 一、@EnableAutoConfiguration注解 ​

1.1 注解定义 ​

@EnableAutoConfiguration是SpringBoot自动配置的入口注解:

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 {};
}

关键点:

  • @AutoConfigurationPackage:自动配置包扫描
  • @Import(AutoConfigurationImportSelector.class):导入配置选择器
  • exclude/excludeName:排除特定的自动配置类

1.2 @AutoConfigurationPackage ​

java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

@AutoConfigurationPackage的作用是将标注该注解的类所在的包及其子包纳入Spring的组件扫描范围。

1.3 AutoConfigurationImportSelector ​

AutoConfigurationImportSelector是实现自动配置的核心类:

java
public class AutoConfigurationImportSelector 
        implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, 
        Ordered {
    
    // ...
    
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationEntry autoConfigurationEntry = 
                getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

🎯 二、自动配置类加载流程 ​

2.1 加载spring.factories文件 ​

核心方法:

java
protected AutoConfigurationEntry getAutoConfigurationEntry(
        AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}

getCandidateConfigurations方法:

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;
}

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
    return EnableAutoConfiguration.class;
}

SpringFactoriesLoader.loadFactoryNames:

java
public static List<String> loadFactoryNames(Class<?> factoryType, 
        @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoaderToUse == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, 
            Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }
    
    result = new HashMap<>();
    try {
        Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
        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();
                String[] factoryImplementationNames = 
                        StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                for (String factoryImplementationName : factoryImplementationNames) {
                    result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
                            .add(factoryImplementationName.trim());
                }
            }
        }
        
        result.replaceAll((factoryType, implementations) -> 
                implementations.stream().distinct().collect(
                        Collectors.collectingAndThen(Collectors.toList(), 
                                Collections::unmodifiableList)));
        cache.put(classLoader, result);
    }
    catch (IOException ex) {
        throw new IllegalArgumentException(
                "Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
    return result;
}

FACTORIES_RESOURCE_LOCATION常量:

java
public static final String FACTORIES_RESOURCE_LOCATION = 
        "META-INF/spring.factories";

spring.factories文件示例:

properties
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
# ... 更多配置类

2.2 自动配置类过滤 ​

过滤流程:

ConfigurationClassFilter过滤:

java
private ConfigurationClassFilter getConfigurationClassFilter() {
    if (this.configurationClassFilter == null) {
        List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
        for (AutoConfigurationImportFilter filter : filters) {
            invokeAwareMethods(filter);
        }
        this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
    }
    return this.configurationClassFilter;
}

protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
    return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
            this.beanClassLoader);
}

AutoConfigurationImportFilter:

java
@FunctionalInterface
public interface AutoConfigurationImportFilter {
    boolean[] match(String[] autoConfigurationClasses, 
            AutoConfigurationMetadata autoConfigurationMetadata);
}

常见的过滤器:

  • OnClassCondition:检查类是否存在
  • OnBeanCondition:检查Bean是否存在
  • OnWebApplicationCondition:检查Web应用类型

🎯 三、自动配置类结构 ​

3.1 自动配置类示例 ​

以DataSourceAutoConfiguration为例:

java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "javax.sql.DataSource")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
        DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
    
    @Configuration(proxyBeanMethods = false)
    @Conditional(EmbeddedDatabaseCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import({ EmbeddedDataSourceConfiguration.class, 
            EmbeddedDataSourcePoolMetadataProvidersConfiguration.class })
    protected static class EmbeddedDatabaseConfiguration {
    }
    
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(HikariDataSource.class)
    @ConditionalOnMissingBean(DataSource.class)
    @ConditionalOnProperty(name = "spring.datasource.type", 
            havingValue = "com.zaxxer.hikari.HikariDataSource", matchIfMissing = true)
    static class Hikari {
        // ...
    }
    
    // ... 更多内部配置类
}

自动配置类特点:

  • @Configuration:标识为配置类
  • @ConditionalOnClass:条件:存在指定的类
  • @ConditionalOnMissingBean:条件:不存在指定的Bean
  • @EnableConfigurationProperties:启用配置属性绑定
  • @Import:导入其他配置类

3.2 条件注解应用 ​

OnClassCondition评估:

java
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
        AnnotatedTypeMetadata metadata) {
    ClassLoader classLoader = context.getClassLoader();
    ConditionMessage matchMessage = ConditionMessage.empty();
    List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
    if (onClasses != null) {
        List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
        if (!missing.isEmpty()) {
            return ConditionOutcome.noMatch(
                    ConditionMessage.forCondition(ConditionalOnClass.class)
                            .didNotFind("required class", "required classes")
                            .items(Style.QUOTE, missing));
        }
        matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
                .found("required class", "required classes")
                .items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
    }
    // ...
    return ConditionOutcome.match(matchMessage);
}

条件判断流程图:

🎯 四、自动配置类加载时机 ​

4.1 加载时机 ​

自动配置类在refresh()方法的invokeBeanFactoryPostProcessors阶段被加载:

java
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, 
            getBeanFactoryPostProcessors());
    
    // ...
}

4.2 ConfigurationClassPostProcessor ​

ConfigurationClassPostProcessor负责处理@Configuration类:

java
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();
    
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
            // ...
        }
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, 
                this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    
    // ...
    
    ConfigurationClassParser parser = new ConfigurationClassParser(
            this.metadataReaderFactory, this.problemReporter, this.environment,
            this.resourceLoader, this.componentScanBeanNameGenerator, registry);
    
    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
    do {
        parser.parse(candidates);
        parser.validate();
        
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);
        
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(registry, 
                    this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
        // ...
    }
    while (!candidates.isEmpty());
}

parse方法:

java
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && 
                    ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        catch (BeanDefinitionStoreException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanDefinitionStoreException(
                    "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
        }
    }
    this.deferredImportSelectorHandler.process();
}

关键:this.deferredImportSelectorHandler.process()

这是处理DeferredImportSelector(包括AutoConfigurationImportSelector)的地方。

🎯 五、自动配置类实例化 ​

5.1 Bean定义注册 ​

自动配置类被解析后,其中的@Bean方法会被转换为BeanDefinition并注册到BeanFactory。

5.2 Bean实例化 ​

在finishBeanFactoryInitialization阶段,自动配置类中定义的Bean会被实例化。

🎯 六、自动配置调试 ​

6.1 开启调试日志 ​

application.yml配置:

yaml
logging:
  level:
    org.springframework.boot.autoconfigure: DEBUG
    root: INFO

6.2 查看自动配置报告 ​

开启条件报告:

yaml
debug: true

这会打印所有自动配置类的条件评估结果。

6.3 断点调试 ​

关键断点位置:

java
// 1. 加载spring.factories
SpringFactoriesLoader.loadFactoryNames(...)

// 2. 选择配置类
AutoConfigurationImportSelector.selectImports(...)

// 3. 条件评估
OnClassCondition.getMatchOutcome(...)

// 4. 解析配置类
ConfigurationClassParser.parse(...)

📖 学习资源 ​

视频教程 ​

书籍推荐 ​

  • 《Spring Boot实战》 - 汪云飞
  • 《Spring Boot 2精髓》 - 李家智
  • 《深入理解Spring Boot》 - 张卫滨

网站资源 ​

✅ 学习检查清单 ​

  • [ ] 已理解@EnableAutoConfiguration注解的作用
  • [ ] 已理解spring.factories文件的加载机制
  • [ ] 已理解AutoConfigurationImportSelector的工作原理
  • [ ] 已理解自动配置类的过滤流程
  • [ ] 已理解条件注解的评估机制
  • [ ] 已理解自动配置类的加载时机
  • [ ] 已掌握自动配置的调试方法

上一章节:启动流程深度解析
下一章节:Starter机制
返回目录:SpringBoot源码深度解析