Spring Boot 自动配置核心:AutoConfigurationImportSelector 深度解析

一、概述

AutoConfigurationImportSelector 是 Spring Boot 自动配置机制的核心组件之一,它负责在应用启动时自动发现、加载和筛选需要生效的自动配置类。这个类实现了 DeferredImportSelector 接口,采用延迟导入策略,确保自动配置类在其他配置类处理完成后再进行导入。

二、类的职责与接口实现

2.1 核心接口

AutoConfigurationImportSelector 实现了以下关键接口:

  • DeferredImportSelector:延迟导入选择器,允许在配置类处理完成后才导入自动配置类
  • BeanClassLoaderAware:获取 Bean 类加载器
  • ResourceLoaderAware:获取资源加载器
  • BeanFactoryAware:获取 Bean 工厂
  • EnvironmentAware:获取环境配置
  • Ordered :定义执行顺序(值为 2147483646,优先级较低)

2.2 主要职责

  1. META-INF/spring.factories 文件中加载自动配置类列表
  2. 对自动配置类进行去重处理
  3. 根据排除规则移除不需要的配置类
  4. 通过过滤器进行条件筛选
  5. 触发自动配置导入事件

三、从 spring.factories 获取自动配置类列表

3.1 核心方法:getCandidateConfigurations

java 复制代码
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
        this.getSpringFactoriesLoaderFactoryClass(), 
        this.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;
}

3.2 工作原理

  1. 指定工厂类类型

    • getSpringFactoriesLoaderFactoryClass() 返回 EnableAutoConfiguration.class
    • 这告诉 SpringFactoriesLoader 要查找哪个键对应的配置类列表
  2. 加载机制

    • SpringFactoriesLoader.loadFactoryNames() 会扫描所有 jar 包中的 META-INF/spring.factories 文件
    • 查找键为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的配置
    • 返回所有符合条件的自动配置类的全限定名列表
  3. 典型的 spring.factories 文件格式

properties 复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.AutoConfiguration1,\
com.example.AutoConfiguration2,\
...

3.3 获取流程

less 复制代码
@EnableAutoConfiguration 注解
    ↓
AutoConfigurationImportSelector.selectImports()
    ↓
getAutoConfigurationEntry()
    ↓
getCandidateConfigurations()
    ↓
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, classLoader)
    ↓
扫描所有 META-INF/spring.factories 文件
    ↓
返回自动配置类全限定名列表

四、自动配置类的筛选和过滤机制

自动配置类的筛选和过滤是一个多步骤的过程,在 getAutoConfigurationEntry 方法中完成:

java 复制代码
protected AutoConfigurationEntry getAutoConfigurationEntry(
    AutoConfigurationMetadata autoConfigurationMetadata, 
    AnnotationMetadata annotationMetadata) {
    
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    
    // 1. 获取注解属性
    AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
    
    // 2. 获取候选配置类列表
    List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
    
    // 3. 去重
    configurations = this.removeDuplicates(configurations);
    
    // 4. 获取排除列表
    Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
    
    // 5. 检查排除类的有效性
    this.checkExcludedClasses(configurations, exclusions);
    
    // 6. 移除排除的配置类
    configurations.removeAll(exclusions);
    
    // 7. 条件过滤
    configurations = this.filter(configurations, autoConfigurationMetadata);
    
    // 8. 触发导入事件
    this.fireAutoConfigurationImportEvents(configurations, exclusions);
    
    return new AutoConfigurationEntry(configurations, exclusions);
}

4.1 步骤一:去重处理

java 复制代码
protected final <T> List<T> removeDuplicates(List<T> list) {
    return new ArrayList(new LinkedHashSet(list));
}
  • 使用 LinkedHashSet 去除重复的配置类
  • 保持原有的顺序(LinkedHashSet 是有序的)

4.2 步骤二:排除机制

排除机制通过 getExclusions 方法收集所有需要排除的配置类:

java 复制代码
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    Set<String> excluded = new LinkedHashSet();
    
    // 1. 从注解的 exclude 属性获取(类对象)
    excluded.addAll(this.asList(attributes, "exclude"));
    
    // 2. 从注解的 excludeName 属性获取(类名)
    excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
    
    // 3. 从配置属性 spring.autoconfigure.exclude 获取
    excluded.addAll(this.getExcludeAutoConfigurationsProperty());
    
    return excluded;
}

排除来源

  1. 注解属性 exclude

    java 复制代码
    @EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
  2. 注解属性 excludeName

    java 复制代码
    @EnableAutoConfiguration(excludeName = {"com.example.SomeAutoConfiguration"})
  3. 配置文件属性

    properties 复制代码
    spring.autoconfigure.exclude=com.example.AutoConfiguration1,com.example.AutoConfiguration2

有效性检查

java 复制代码
private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
    List<String> invalidExcludes = new ArrayList(exclusions.size());
    
    for (String exclusion : exclusions) {
        // 如果类存在但不在配置列表中,说明排除无效
        if (ClassUtils.isPresent(exclusion, this.getClass().getClassLoader()) 
            && !configurations.contains(exclusion)) {
            invalidExcludes.add(exclusion);
        }
    }
    
    if (!invalidExcludes.isEmpty()) {
        this.handleInvalidExcludes(invalidExcludes);
    }
}

4.3 步骤三:条件过滤(核心机制)

filter 方法是自动配置类筛选的核心,它使用 AutoConfigurationImportFilter 进行条件判断:

java 复制代码
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
    long startTime = System.nanoTime();
    String[] candidates = StringUtils.toStringArray(configurations);
    boolean[] skip = new boolean[candidates.length];
    boolean skipped = false;
    
    // 获取所有过滤器
    Iterator<AutoConfigurationImportFilter> iterator = 
        this.getAutoConfigurationImportFilters().iterator();
    
    while (iterator.hasNext()) {
        AutoConfigurationImportFilter filter = iterator.next();
        
        // 注入依赖(Aware 接口)
        this.invokeAwareMethods(filter);
        
        // 执行过滤匹配
        boolean[] match = filter.match(candidates, autoConfigurationMetadata);
        
        // 标记需要跳过的配置类
        for (int i = 0; i < match.length; ++i) {
            if (!match[i]) {
                skip[i] = true;
                candidates[i] = null;
                skipped = true;
            }
        }
    }
    
    // 如果没有被过滤的,直接返回
    if (!skipped) {
        return configurations;
    }
    
    // 收集未被过滤的配置类
    List<String> result = new ArrayList(candidates.length);
    for (int i = 0; i < candidates.length; ++i) {
        if (!skip[i]) {
            result.add(candidates[i]);
        }
    }
    
    if (logger.isTraceEnabled()) {
        int numberFiltered = configurations.size() - result.size();
        logger.trace("Filtered " + numberFiltered + " auto configuration class in " + 
            TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
    }
    
    return new ArrayList(result);
}

过滤器获取

java 复制代码
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
    return SpringFactoriesLoader.loadFactories(
        AutoConfigurationImportFilter.class, 
        this.beanClassLoader
    );
}

过滤器同样通过 spring.factories 加载,典型的过滤器包括:

  • OnClassCondition:检查类路径中是否存在指定的类
  • OnBeanCondition:检查容器中是否存在指定的 Bean
  • OnWebApplicationCondition:检查是否为 Web 应用

过滤器工作原理

  1. 每个过滤器对候选配置类数组进行匹配
  2. 返回一个 boolean[] 数组,true 表示匹配(保留),false 表示不匹配(过滤)
  3. 所有过滤器的结果进行逻辑与运算(所有过滤器都通过才保留)

示例:OnClassCondition 过滤器

java 复制代码
// 伪代码示例
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata metadata) {
    boolean[] match = new boolean[autoConfigurationClasses.length];
    
    for (int i = 0; i < autoConfigurationClasses.length; i++) {
        String className = autoConfigurationClasses[i];
        
        // 检查 @ConditionalOnClass 注解
        // 如果类路径中不存在必需的类,则返回 false
        match[i] = checkRequiredClassesExist(className);
    }
    
    return match;
}

4.4 步骤四:触发导入事件

java 复制代码
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    List<AutoConfigurationImportListener> listeners = 
        this.getAutoConfigurationImportListeners();
    
    if (!listeners.isEmpty()) {
        AutoConfigurationImportEvent event = 
            new AutoConfigurationImportEvent(this, configurations, exclusions);
        
        for (AutoConfigurationImportListener listener : listeners) {
            this.invokeAwareMethods(listener);
            listener.onAutoConfigurationImportEvent(event);
        }
    }
}

允许其他组件监听自动配置导入过程,进行额外的处理。

五、延迟导入机制(DeferredImportSelector)

5.1 AutoConfigurationGroup 内部类

AutoConfigurationImportSelector 实现了 getImportGroup 方法,返回 AutoConfigurationGroup

java 复制代码
public Class<? extends DeferredImportSelector.Group> getImportGroup() {
    return AutoConfigurationGroup.class;
}

5.2 延迟导入的优势

  1. 优先级控制:确保自动配置类在其他配置类之后处理
  2. 条件评估:在 Bean 定义完成后,条件注解能更准确地评估
  3. 性能优化:避免不必要的类加载和条件检查

5.3 排序机制

AutoConfigurationGroup.selectImports() 中,会对配置类进行排序:

java 复制代码
private List<String> sortAutoConfigurations(
    Set<String> configurations, 
    AutoConfigurationMetadata autoConfigurationMetadata) {
    
    return (new AutoConfigurationSorter(
        this.getMetadataReaderFactory(), 
        autoConfigurationMetadata
    )).getInPriorityOrder(configurations);
}

排序依据:

  • @AutoConfigureBefore 注解
  • @AutoConfigureAfter 注解
  • @AutoConfigureOrder 注解
  • 依赖关系

六、完整流程图

less 复制代码
应用启动
    ↓
@EnableAutoConfiguration 注解被处理
    ↓
AutoConfigurationImportSelector.selectImports()
    ↓
┌─────────────────────────────────────┐
│ getAutoConfigurationEntry()         │
│                                     │
│ 1. 检查是否启用自动配置              │
│ 2. 从 spring.factories 加载候选类   │
│ 3. 去重                             │
│ 4. 获取排除列表                     │
│ 5. 移除排除的配置类                 │
│ 6. 条件过滤(OnClassCondition等)   │
│ 7. 触发导入事件                     │
└─────────────────────────────────────┘
    ↓
返回最终配置类列表
    ↓
Spring 容器加载这些配置类
    ↓
自动配置生效
相关推荐
G***T6911 小时前
Java设计模式之责任链
设计模式
武子康1 小时前
大数据-161 Apache Kylin Cube 实战:建模、构建与查询加速完整指南
大数据·后端·apache kylin
踏浪无痕1 小时前
准备手写Simple Raft(三) 日志复制——一致性检查
后端·raft
星轨初途1 小时前
数据结构二叉树之链式结构(3)(下)
c语言·网络·数据结构·经验分享·笔记·后端
6***x5451 小时前
Java设计模式之策略模式
java·设计模式·策略模式
miss_you12131 小时前
策略模式 + 模板方法 + 注册式工厂 统一设计方案(营销优惠场景示例)
设计模式·工厂方法模式·策略模式·模板方法模式
章鱼哥7301 小时前
Java 策略模式 + 聚合对象:实现多模块的统计与聚合,快速扩展的实战
java·开发语言·策略模式
h***59332 小时前
SpringBoot中如何手动开启事务
java·spring boot·spring
倚肆2 小时前
Java泛型详解:尖括号<>、通配符?与类型参数T
java