Spring Boot 自动装配底层源码实现详解

🌟 Spring Boot 自动装配底层源码实现详解

✅ 本文深入源码,完整剖析 Spring Boot 自动装配背后的实现逻辑,适合有一定基础的开发者查漏补缺、面试复习。


✅ 总结成流程图(详细调用链)

text 复制代码
@SpringBootApplication
     │
     ▼
@EnableAutoConfiguration                           ⬅️ 自动装配启动入口
     │
     ▼
@Import(AutoConfigurationImportSelector.class)     ⬅️ 导入自动配置选择器类
     │
     ▼
SpringApplication.run()
     │
     ▼
ApplicationContext.refresh()                       ⬅️ 启动容器
     │
     ▼
invokeBeanFactoryPostProcessors()                  ⬅️ 调用所有 BeanFactory 后处理器
     │
     ▼
ConfigurationClassPostProcessor                    ⬅️ 处理配置类
     │
     ▼
parse()                                            ⬅️ 解析配置类
     │
     ▼
ConfigurationClassParser
     │
     ├── scan() / processConfigurationClass()
     │
     └── processImports()                          ⬅️ 处理 @Import 注解
              │
              ▼
   AutoConfigurationImportSelector                 ⬅️ 自动装配选择器
              │
              ▼
   selectImports(annotationMetadata)               ⬅️ 返回配置类路径数组
              │
              ├── getAutoConfigurationEntry()
              │        ├── getCandidateConfigurations()
              │        │        ├── SpringFactoriesLoader.loadFactoryNames()
              │        │        │        └── loadSpringFactories()        ⬅️ 加载 spring.factories
              │        │        └── ImportCandidates.load()              ⬅️ 加载 .imports 文件
              │        └── 去重、排除、过滤、事件发布等处理
              │
              ▼
   注册自动配置类为 @Configuration 配置类
     │
     ▼
loadBeanDefinitions()                              ⬅️ 将 @Bean 方法注册为 BeanDefinition
     │
     ▼
容器初始化,自动配置类生效

🧠 自动装配的核心流程分为 5 步:

自动配置类加载主要涉及三个类:AutoConfigurationImportSelectorSpringFactoriesLoaderConfigurationClassBeanDefinitionReader

  • AutoConfigurationImportSelectorSpringFactoriesLoader:这两个类主要用来加载第三方自动装配配置类的全类名
  • ConfigurationClassBeanDefinitionReader:主要将获取到的全类名加载成 bean

🥇 1. 启动入口加载配置类

java 复制代码
SpringApplication.run(App.class, args);
  • 启动 Spring 容器
  • 自动注册 ConfigurationClassPostProcessor(处理配置类的后置处理器)

🥈 2. @EnableAutoConfiguration 被解析,回调其 AutoConfigurationImportSelector类selectImports() 方法

✅ 加载第三方自动装配配置类的全类名背后逻辑
  • @EnableAutoConfiguration 内部通过 @Import(AutoConfigurationImportSelector.class) 导入配置类选择器
  • 在配置类解析阶段,Spring 会回调其 AutoConfigurationImportSelector的selectImports() 方法。
  • 依赖AutoConfigurationImportSelectorSpringFactoriesLoader:这两个类来加载第三方自动装配配置类的全类名
🔎 2.1 AutoConfigurationImportSelector类解析
selectImports() 方法详解
java 复制代码
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        AutoConfigurationEntry entry = this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(entry.getConfigurations());
    }
}

作用 :调用 getAutoConfigurationEntry 获取配置类路径,然后返回给 Spring 进行后续解析。

📦 其中AutoConfigurationEntry 结构:
java 复制代码
protected static class AutoConfigurationEntry {
    private final List<String> configurations; // 要注入的配置类
    private final Set<String> exclusions;      // 要排除的类
}

getAutoConfigurationEntry() 方法详解
java 复制代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);

        // ① 加载所有候选配置类路径
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);

        // ② 去重
        configurations = this.<String>removeDuplicates(configurations);

        // ③ 获取排除的类(通过exclude属性或@EnableAutoConfiguration(exclude = ...)配置)
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        this.checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);

        // ④ 过滤无效配置类(比如条件不满足的 @ConditionalOnClass)
        configurations = this.getConfigurationClassFilter().filter(configurations);

        // ⑤ 发布事件通知监听器
        this.fireAutoConfigurationImportEvents(configurations, exclusions);

        return new AutoConfigurationEntry(configurations, exclusions);
    }
}

作用总结

  • 负责真正收集所有需要自动装配的配置类
  • 做了去重、排除、过滤、事件通知等处理

获取配置类路径 → getCandidateConfigurations()
java 复制代码
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = new ArrayList<>(
    // **获取文件配置类**
        SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader())
    );

    // Spring Boot 2.7+ 新增的支持 .imports 文件配置方式
    ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader())
                    .forEach(configurations::add);

    Assert.notEmpty(configurations,
        "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. " +
        "If you are using a custom packaging, make sure that file is correct.");

    return configurations;
}

作用总结

  • 在configurations后继续追加 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports(为了适应2.7+后的配置文件)自动配置类的路径
📦 2.2 SpringFactoriesLoader类 源码解析
🔧 loadFactoryNames() 方法
java 复制代码
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = (classLoader != null) ? classLoader : SpringFactoriesLoader.class.getClassLoader();
    // factoryTypeName :org.springframework.boot.autoconfigure.EnableAutoConfiguration
    String factoryTypeName = factoryType.getName();
    // 根据key返回对应的第三方配置类的全路径名,其中
    return (List) loadSpringFactories(classLoaderToUse)
                  .getOrDefault(factoryTypeName, Collections.emptyList());
}
🔧 loadSpringFactories() 方法
java 复制代码
// 加载 META-INF/spring.factories文件成对应的map
// 其中: key:org.springframework.boot.autoconfigure.EnableAutoConfiguration
// value:com.alibaba.boot.nacos.config.autoconfigure.NacosConfigAutoConfiguration 
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("META-INF/spring.factories");

        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);

            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String key = ((String) entry.getKey()).trim();
                String[] valueList = StringUtils.commaDelimitedListToStringArray((String) entry.getValue());

                for (String value : valueList) {
                    result.computeIfAbsent(key, k -> new ArrayList<>()).add(value.trim());
                }
            }
        }

        // 去重、封装成不可变集合
        result.replaceAll((factoryType, implementations) ->
            implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))
        );

        cache.put(classLoader, result);
        return result;
    } catch (IOException ex) {
        throw new IllegalArgumentException(\"Unable to load factories from location [META-INF/spring.factories]\", ex);
    }
}

作用总结

  • 从类路径中加载 META-INF/spring.factories
  • 将其转为 Map<String, List<String>> 缓存并返回
  • 自动装配类的注册入口:key 是接口,value 是其实现类路径

🏅 3. 返回的配置类名 → 注册为配置类

  • 上一步中 selectImports() 返回的是 List<String> 类型的配置类路径
  • Spring 会将其视为 @Configuration 进行进一步解析

🏆 4. 配置类的 @Bean 方法 → 注册为 BeanDefinition

ConfigurationClassBeanDefinitionReader 完成:

  • 注册配置类本身为 Bean
  • 注册其 @Bean 方法为 loadBeanDefinitions
  • 最终注册到 DefaultListableBeanFactory
    我们来逐步解读Bean注册 loadBeanDefinitions 源码方法,它们位于 ConfigurationClassBeanDefinitionReader 类中,是 将解析后的配置类注册为 BeanDefinition 的核心流程。

🔍 方法一:loadBeanDefinitions()

java 复制代码
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();

    for (ConfigurationClass configClass : configurationModel) {
        this.loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}
✨ 作用:

这是入口方法,接收一批配置类(@Configuration@Import 导入的类等),并逐个调用内部的 loadBeanDefinitionsForConfigurationClass() 方法进行处理。

🧠 参数说明:
  • configurationModel:Spring 通过 ConfigurationClassParser 解析得到的完整配置类集合。
  • TrackedConditionEvaluator:用于判断配置类是否应被跳过(基于 @Conditional)。

🔍 方法二:loadBeanDefinitionsForConfigurationClass()

java 复制代码
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        // 1️⃣ 如果该配置类不应被注册(如被 @Conditional 排除),则移除原有的 BeanDefinition(如果存在)
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }

        // 同时移除该类对应的 Import 注册信息
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
    } else {
        // 2️⃣ 注册配置类本身为 BeanDefinition(如果是被 @Import 导入的)
        if (configClass.isImported()) {
            this.registerBeanDefinitionForImportedConfigurationClass(configClass);
        }

        // 3️⃣ 注册 @Bean 方法对应的 BeanDefinition
        for (BeanMethod beanMethod : configClass.getBeanMethods()) {
            this.loadBeanDefinitionsForBeanMethod(beanMethod);
        }

        // 4️⃣ 注册 @ImportResource 引用的外部配置文件(如 XML)
        this.loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

        // 5️⃣ 注册通过 ImportBeanDefinitionRegistrar 动态注册的 Bean
        this.loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }
}

✅ 小结:整个方法做了 5 件事

步骤 作用 方法
1️⃣ 判断是否跳过配置类(@Conditional shouldSkip(configClass)
2️⃣ 注册被 @Import 导入的配置类 registerBeanDefinitionForImportedConfigurationClass(configClass)
3️⃣ 注册配置类中声明的 @Bean 方法 loadBeanDefinitionsForBeanMethod(beanMethod)
4️⃣ 注册 XML 等外部资源 loadBeanDefinitionsFromImportedResources()
5️⃣ 注册通过 ImportBeanDefinitionRegistrar 动态生成的 Bean loadBeanDefinitionsFromRegistrars()

🧪 5. 容器初始化,实例化 Bean

容器刷新阶段:

  • 根据已注册的 BeanDefinition 实例化 Bean
  • 自动配置类也就被激活(如:数据源、Redis、WebMvc 等)

📌 小结

阶段 核心方法 作用
启动 SpringApplication.run() 启动容器
自动装配发现 selectImports() 获取自动配置类名
解析配置类 getAutoConfigurationEntry() 去重、过滤、排除
加载配置来源 loadFactoryNames() / loadSpringFactories() 从 spring.factories 加载类路径
注册与初始化 loadBeanDefinitions() 注册 @Bean 到 BeanFactory

🎉 总结一句话:

Spring Boot 自动装配的核心就是:@EnableAutoConfiguration + SpringFactoriesLoader 读取配置类名并注册为 Bean!

相关推荐
Aspartame~8 分钟前
企业级WEB应用服务器TOMCAT
java·运维·服务器·tomcat
陈佬昔没带相机11 分钟前
围观前后端对接的 TypeScript 最佳实践,我们缺什么?
前端·后端·api
你我约定有三17 分钟前
分布式微服务--万字详解 微服务的各种负载均衡全场景以注意点
java·开发语言·windows·分布式·微服务·架构·负载均衡
奈斯。zs17 分钟前
java面向对象高级02——单例类(设计模式)
java·开发语言·设计模式
拾荒的小海螺36 分钟前
Redis:缓存雪崩、穿透、击穿的技术解析和实战方案
java·redis·缓存
旋风菠萝1 小时前
JVM易混淆名称
java·jvm·数据库·spring boot·redis·面试
雨叶微枫1 小时前
高效编解码协议之protobuf协议详解
java
Livingbody2 小时前
大模型微调数据集加载和分析
后端
Livingbody2 小时前
第一次免费使用A800显卡80GB显存微调Ernie大模型
后端
77qqqiqi2 小时前
解决Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplate‘ are required报错问题
java·数据库·微服务·mybatis·mybatisplus