本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。

SpringBoot 源码分析

本文基于 SpringBoot 2.3.4.STABLE 版本撰写

1) 依赖管理

2) Maven parent 依赖引入

2.1) spring-boot-starter-parent 分析

一个最基本的 SpringBoot 工程中, 会在 pom.xml 中引入一个 parent 依赖: spring-boot-starter-parent, 观察此依赖包内容如下:

  • spring-boot-starter-parent-2.3.4.RELEASE.pom
  1. 只有一个 pom 文件, 是一个 pom 类型的依赖, 打开: spring-boot-starter-parent-2.3.4.RELEASE.pom 后可以发现引入了一些 Plugin, 没有啥有用的信息.
  2. 翻到头部找找有无父依赖, 发现其还有一个父依赖 spring-boot-dependencies, 此依赖便是 spring-boot-starter-parent 的核心实现:
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.3.4.RELEASE</version>
    </parent>
2.1.1) spring-boot-dependencies 分析

老样子先来研究一下目录结构:

  • spring-boot-dependencies-2.3.4.RELEASE.pom

发现也是一个 pom 依赖, 打开 spring-boot-dependencies-2.3.4.RELEASE.pom 后一幕明了了, 结构大致如下:

<!-->1. 首先定义了一些 properties 的版本号</-->
  <properties>
    <activemq.version>5.15.13</activemq.version>
    <antlr2.version>2.7.7</antlr2.version>
    <appengine-sdk.version>1.9.82</appengine-sdk.version>
    <!-->.......</-->
    <!-->.......</-->
  </properties>

<!-->2. 接着是基于 properties 中的版本号, 利用 maven-dependency-management 管理相应包的依赖版本</-->
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-amqp</artifactId>
        <version>${activemq.version}</version>
      </dependency>
      <dependency>
        <groupId>org.apache.activemq</groupId>
        <artifactId>activemq-blueprint</artifactId>
        <version>${activemq.version}</version>
      </dependency>
      <!-->.......</-->
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.3.4.RELEASE</version>
      </dependency>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-activemq</artifactId>
        <version>2.3.4.RELEASE</version>
      </dependency>
      <!-->.......</-->
    </dependencies>
  </dependencyManagement>
<!-->3. 最后利用 maven-plugin-management 管理插件依赖的版本</-->
  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>build-helper-maven-plugin</artifactId>
          <version>${build-helper-maven-plugin.version}</version>
        </plugin>
        <plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>exec-maven-plugin</artifactId>
          <version>${exec-maven-plugin.version}</version>
        </plugin>
        <!-->.......</-->
        <!-->.......</-->
    </pluginManagement>
  </build>

总结: spring-boot-starter-parent 中, 利用 maven 的依赖管理, 管理了一些常用包以及 starter 的依赖版本号, 起到了版本号统一管理的作用, 版本号可以提供给 SpringBoot 的其他 starter使用, 起到了让我们引入一些基础依赖或 starter 时, 不需要手动填写版本号的作用.

2.2) spring-boot-starter-web 分析

上一节讲到 spring-boot-starter-parent 主要起到统一管理常用包的一些版本号, 我们拿最常用的依赖包 spring-boot-starter-web 来接着分析一下, 找到包目录中文件如下:

  • spring-boot-starter-web-2.3.4.RELEASE.jar
  • spring-boot-starter-web-2.3.4.RELEASE.pom

jar 包中只有一些 License 文件, 打开 pom 文件康康带进了啥依赖:

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.2.9.RELEASE</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.9.RELEASE</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>

观察发现带入了以下几种传递依赖:

  1. SpringBoot 基础库
  2. Spring json 相关库
  3. Tomcat
  4. SpringMVC

总结: spring-boot-starter-web 将 WEB 相关的依赖做了一个打包, 简化项目中 pom 文件的长度, 增强可读性.

3) SpringBoot 自动配置

从一个最基础的启动类开始讲起吧, 本节先讲讲 @SpringBootApplication 注解, 其注解最主要的作用时自动配置, 或者叫自动装配:

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication application = new SpringApplication(Application.class);
    application.run(args);
  }
}

将不重要或基础的代码移除后, @SpringBootApplication 中代码大致如下, 将分为三个小节讲解:

// 注解作用域等
@SpringBootConfiguration // 3.1
@EnableAutoConfiguration // 3.2 
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
  // 注解字段属性
}

3.1) @SpringBootConfiguration

在当前版本中, 此注解只是为我们的启动类, 加上了 @Configuration 注解, 标记为 SpringFramework 的配置类, 代码:

@Configuration
public @interface SpringBootConfiguration {
  // ...
}

3.2) @EnableAutoConfiguration

@EnableAutoConfiguration 中的代码就是重头戏了, SpringBoot 自动配置主要就是基于此注解实现, 来康康代码, 两句注解将通过 3.2.1, 3.2.2 两小节讲解:

@AutoConfigurationPackage // 3.2.1
@Import(AutoConfigurationImportSelector.class) // 3.2.2
public @interface EnableAutoConfiguration {
  // ...
}
3.2.1) @AutoConfigurationPackage

我们先来康康代码:

@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
  // ...
}

代码量很少, @Import 注解可以将类导入 Spring 容器, 我们接着看看这个内部类 Registrar:

  // 实现了 ImportBeanDefinitionRegistrar, 代表此类使用个性化加载, 并不将 Registrar 本身注入容器
    static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

        @Override
        public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
      // 1. new PackageImports(metadata).getPackageNames().toArray(new String[0])
      // 返回了当前启动类所在的包的包名
      // 2. 接着将 registry Bean 注册表, 和上一步获取的包名传入 register 方法
            register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
        }

    // determineImports 方法在 SpringBoot 启动过程中没有调用, 国内外也基本查不到相关作用, 就不讲了
    // 官方 Doc 里注释此方法返回一个需要注入到容器的对象数组
        @Override
        public Set<Object> determineImports(AnnotationMetadata metadata) {
            return Collections.singleton(new PackageImports(metadata));
        }
    }

我们接着来康康 register 方法:

  // 1. org.springframework.boot.autoconfigure.AutoConfigurationPackages
    private static final String BEAN = AutoConfigurationPackages.class.getName();

    public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    // 2. Spring 容器中是否包含 AutoConfigurationPackages
        if (registry.containsBeanDefinition(BEAN)) { // 启动过程中并不包含, 继续走 else
            BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
            ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
            constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
        }
        else {
      // 3. 定义一个标准 Bean, 相当于需要注入容器的 Bean 的包装类
            GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
      // 4. 定义需要注入容器的类
            beanDefinition.setBeanClass(BasePackages.class);
      // 5. 根据其第 1 个构造方法, 传入启动类所在包名作为构造器参数, 后续会根据此构造方法反射构建 Bean
            beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
            beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 4. 表明此 Bean 为 Spring 后台注册而非人为注册
      // 5. 注入容器, name 为 org.springframework.boot.autoconfigure.AutoConfigurationPackages
            registry.registerBeanDefinition(BEAN, beanDefinition);
        }
    }

总结: 很多人以为 register 这个方法会扫描启动类所有子包, 并将 @Component 等注解类标注的类加入容器, 实际上只是将启动类所在包名的包装类, 加入了容器中, BeanName 为: org.springframework.boot.autoconfigure.AutoConfigurationPackages

是的, @Import(AutoConfigurationPackages.Registrar.class) 注解几乎啥也没做

3.2.1) @Import(AutoConfigurationImportSelector.class)

熟悉的 @Import, 我们进入 Selector 康康:

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
        ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
    // ...
}
  1. 发现此类实现的是 DeferredImportSelector, 它也是一个个性化加载接口, 实现它会在所有的 @Configuration 注解类处理完才执行导入操作.
  2. 接着实现了其他四个接口: BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, 从这四个接口实现的方法会比 selectImports 方法(DeferredImportSelector)先执行.
  3. 最后还有一个 Ordered, 实现了其 getOrder 方法: public int getOrder() { return Ordered.LOWEST_PRECEDENCE - 1; }, 这里也应证了第 1 点.

我们先来康康从第 2 点中实现下来的方法吧:

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory);
        this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

发现没啥信息, 很基础的实现, 没做啥特殊操作, 我们接着康康从 Selector 中实现的方法:

    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
    // 1. 默认为 True, 可以通过在 SpringBoot 配置文件中将 `spring.boot.enableautoconfiguration=false` 关闭自动装配
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }

先进入 getAutoConfigurationEntry 康康:

    protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    // 1. 默认为 True, 可以通过在 SpringBoot 配置文件中将 `spring.boot.enableautoconfiguration=false` 关闭自动装配
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
    // 2. 获取 @EnableAutoConfiguration 注解中的属性
        AnnotationAttributes attributes = getAttributes(annotationMetadata);
    // 3.3 中讲解
        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);
    }

代码行数过多, 3.3 中详细讲解:

3.3) getAutoConfigurationEntry

  1. getCandidateConfigurations
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    我们进入方法内部康康:

    protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
      // 加载 spring.factories 中, key 为 EnableAutoConfiguration 全类名的 value 们
      List 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;
    }

    阅读过 SpringBoot 启动源码的童鞋可能对这行代码比较熟悉, 这里不深入讲了, 稍微来看看 loadFactoryNames 的源码:

    public static List loadFactoryNames(Class factoryType, @Nullable ClassLoader classLoader) {
      String factoryTypeName = factoryType.getName(); // 拿到 EnableAutoConfiguration 的全类名
      // 从 spring.factories 中读取 key 为 EnableAutoConfiguration 的全类名的值
      return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    最后返回一个自动配置类的 List 集合.

  2. removeDuplicates
    configurations = removeDuplicates(configurations);
    进入方法内部看看:

    protected final  List removeDuplicates(List list) {
      return new ArrayList<>(new LinkedHashSet<>(list));
    }

    这就比较简单了 =.=, 用一个 HashSet 对 List 做了一个去重, 然后重新转成一个 List 集合, removeDuplicates 中主要是对我们的自动配置类做一个去重工作.

  3. getExclusions
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    上面说到, annotationMetadata 是我们启动类的一些元数据信息, attributes 是 @EnableAutoConfiguration 注解中的属性, 我们进入 getExclusions 方法康康:

    protected Set getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
        Set excluded = new LinkedHashSet<>(); // 去重集合
    // 获取 @EnableAutoConfiguration 中的 exclude 属性
        excluded.addAll(asList(attributes, "exclude"));
    // 获取 @EnableAutoConfiguration 中的 excludeName 属性
        excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
    // 获取 SpringBoot 配置文件中的 spring.autoconfigure.exclude 配置的排除类, 这里不深入讲解了
        excluded.addAll(getExcludeAutoConfigurationsProperty());
        return excluded;
    }

    最后返回一个去重后自动配置类的排除类集合, 我们接着往下看.

  4. checkExcludeClasses
    checkExcludedClasses(configurations, exclusions);

    private void checkExcludedClasses(List configurations, Set exclusions) {
        List invalidExcludes = new ArrayList<>(exclusions.size());
        for (String exclusion : exclusions) {
      // 1. 排除类如果存在, 且从 spring.factories 中读取的配置类数组中**不包含**
            if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
                invalidExcludes.add(exclusion); // 2. 塞入 invalidExcludes 异常排除类集合
            }
        }
    // 3. 如有异常排除类进入 handleInvalidExcludes 方法抛出一个异常, 这里应该会终止 SpringBoot 的启动
        if (!invalidExcludes.isEmpty()) {
            handleInvalidExcludes(invalidExcludes); // 不深入讲了
        }
    }
  5. removeALl
    configurations.removeAll(exclusions);
    从自动配置类集合中, 移除掉我们配置的排除类.
  6. getConfigurationClassFilter
    configurations = getConfigurationClassFilter().filter(configurations);
    先来看看 getConfigurationClassFilter() 方法

    private ConfigurationClassFilter getConfigurationClassFilter() {
        if (this.configurationClassFilter == null) {
      // 1. 获取 spring.factories 中的过滤器
            List filters = getAutoConfigurationImportFilters();
            for (AutoConfigurationImportFilter filter : filters) {
                invokeAwareMethods(filter); // 2. 遍历过滤器赋值注入一些环境参数
            }
      // 3. 将过滤器赋值到局部变量
            this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
        }
        return this.configurationClassFilter;
    }

    过滤器的初始化我们分三步来讲解:

    1. 我们先来康康 getAutoConfigurationImportFilters 的过程
      protected List getAutoConfigurationImportFilters() {
      // 还是熟悉的 loadFactories, 略过不讲了
      // 从 spring.factories 中读取 key 为 AutoConfigurationImportFilter.class 的过滤器
      return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
      }
    2. 然后是执行过滤器 invokeAwareMethods(filter);, 康康代码:
      private void invokeAwareMethods(Object instance) {
      if (instance instanceof Aware) {
          if (instance instanceof BeanClassLoaderAware) {
              ((BeanClassLoaderAware) instance).setBeanClassLoader(this.beanClassLoader);
          }
          if (instance instanceof BeanFactoryAware) {
              ((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
          }
          if (instance instanceof EnvironmentAware) {
              ((EnvironmentAware) instance).setEnvironment(this.environment);
          }
          if (instance instanceof ResourceLoaderAware) {
              ((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
          }
      }
      }

      发现只是判断过滤器实现了哪些接口, 注入了一些基本环境参数, 我们回到上一步代码接着往下看看

    3. new ConfigurationClassFilter
      this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);

      ConfigurationClassFilter(ClassLoader classLoader, List filters) {
          this.autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(classLoader);
          this.filters = filters;
      }

    发现只是将 filters 集合包装了一下, 还执行了一个 loadMetadata 方法:

    protected static final String PATH = "META-INF/spring-autoconfigure-metadata.properties";
    
    static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
        return loadMetadata(classLoader, PATH); // 1. 调用了下方的重载方法
    }
    
    static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
        try {
      // 2. 加载 spring-autoconfigure-metadata.properties 文件
            Enumeration urls = (classLoader != null) ? classLoader.getResources(path)
                    : ClassLoader.getSystemResources(path);
      // 3. 将 metadata 文件转成 Properties 格式对象
            Properties properties = new Properties();
            while (urls.hasMoreElements()) {
                properties.putAll(PropertiesLoaderUtils.loadProperties(new UrlResource(urls.nextElement())));
            }
            return loadMetadata(properties); // 4. 再次调用重载方法
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load @ConditionalOnClass location [" + path + "]", ex);
        }
    }
    
    static AutoConfigurationMetadata loadMetadata(Properties properties) {
    // 5. 最后创建了一个 porperties 的包装类返回
        return new PropertiesAutoConfigurationMetadata(properties);
    }
    
    private static class PropertiesAutoConfigurationMetadata implements AutoConfigurationMetadata {
    
    private final Properties properties;
    
    PropertiesAutoConfigurationMetadata(Properties properties) {
      this.properties = properties;
    }
    // ...
    }

    稍微总结一下过滤器的初始化 getConfigurationClassFilter, 将 spring.factories 中的 filter, 和 spring-autoconfigure-metadata.properties 中的每一行数据读取了出来, 包装成了一个对象.

  7. filter
    configurations = getConfigurationClassFilter().filter(configurations);
    那我们接着康康 filter 这个方法:

    List filter(List configurations) {
            long startTime = System.nanoTime();
            String[] candidates = StringUtils.toStringArray(configurations); // 1. 将自动配置类转成 String 数组
            boolean skipped = false;
            for (AutoConfigurationImportFilter filter : this.filters) { // 2. 遍历每一个 filter
        // 3. 将每个 filter 中的 match 方法, 判断自动配置类是否满足启动条件
        //   filter 是一个过滤器, metadata 则是过滤器过滤自动配置类需要依赖的数据
        //   结合过滤器和 metadata 则可以得出符合条件的自动配置类
                boolean[] match = filter.match(candidates, this.autoConfigurationMetadata); // 4. 返回自动配置类是否满足条件的 bool 数组
                for (int i = 0; i < match.length; i++) {
                    if (!match[i]) {
                        candidates[i] = null; // 5. 将不满足自动配置条件的自动配置类删除
                        skipped = true;
                    }
                }
            }
            if (!skipped) {
                return configurations;
            }
      // 6. 最后将满足条件的自动配置类返回
            List result = new ArrayList<>(candidates.length);
            for (String candidate : candidates) {
                if (candidate != null) {
                    result.add(candidate);
                }
            }
            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 result;
        }

    看完简介, 我们结合 spring.factoriesspring-autoconfigure-metadata.properties 接着分析一下 3 和 4 之间的代码流程
    factoriesmetadata 中的自动配置类数据起到一一对应, 从 factories 中读取出的配置类, 在 metadata 中会有一条匹配规则:

    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration=
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass=com.rabbitmq.client.Channel,org.springframework.amqp.rabbit.core.RabbitTemplaten
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$MessagingTemplateConfiguration=
    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration$MessagingTemplateConfiguration.ConditionalOnClass=org.springframework.amqp.rabbit.core.RabbitMessagingTemplate

    比如我需要找 RabbitAutoConfiguration 自动配置类的启动条件, 会从 metadata 中读取出这一行:

    org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration.ConditionalOnClass=com.rabbitmq.client.Channel,org.springframework.amqp.rabbit.core.RabbitTemplaten

    匹配规则大概格式这样: 自动配置类全类名.匹配条件=匹配条件参数
    所以, RabbitAutoConfiguraion 需要满足以下条件才加载: ClassPath 中包含 com.rabbitmq.client.Channel,org.springframework.amqp.rabbit.core.RabbitTemplaten 这两个类.
    匹配条件不止一种, 一共如下:

    • ConditionalOnBean: Spring 容器中是否包含 Bean
    • ConditionalOnClass: ClassPath 中是否包含 Bean
    • ConditionalOnCloudPlatform: 是否运行在指定的云平台上
    • ConditionalOnExpression: SpEL 表达式结果为 true 时
    • ConditionalOnJava: 是否满足指定的 java 版本
    • ConditionalOnJndi: 满足指定的 Jndi
    • ConditionalOnMissingBean: Spring 容器中不存在某些 Bean 时生效
    • ConditionalOnMissingClass: ClassPath 中不存在某些 Class 时生效
    • ConditionalOnNotWebApplication: 非 Web 环境下 SpringBoot 运行时生效
    • ConditionalOnProperty: SpringBoot 参数设置值相等生效
    • ConditionalOnResource: 指定的文件存在时生效
    • ConditionalOnSingleCandidate: 当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean
    • ConditionalOnWarDeployment: 传统 WAR 包部署时生效
    • ConditionalOnWebApplication: SpringBoot 以 Web type 运行时生效
      深入 match 的代码就不讲了, 太过底层繁杂, 略过.
  8. fireAutoConfigurationImportEvents
    private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
    // 1. 从 spring.factories 中读取 key 为 AutoConfigurationImportListener 的监听器集合
        List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
        if (!listeners.isEmpty()) {
      // 2. 将已过滤的自动配置类集合, 和排除类集合(没错,第3次了)构建一个 Spring 事件
            AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
            for (AutoConfigurationImportListener listener : listeners) {
                invokeAwareMethods(listener); // 3. 监听器环境参数注入
                listener.onAutoConfigurationImportEvent(event); // 4. 让监听器执行事件, 没有广播
            }
        }
    }
  9. return
    最后返回自动配置类和排除类集合的包装类.

    return new AutoConfigurationEntry(configurations, exclusions);

    最终向 Spring 返回了过滤后自动配置类的 String 数组, 交由 Spring 执行自动配置类.

        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());

SpringBoot 自动配置/装配的源码大概就这样写完了.

4) 总结

  • Maven 相关
    • parent 中定义了基础包和官方 starter 的版本号
    • 一个 starter 中传递依赖了很多子包
  • @SpringApplication
    1. @SpringBootConfiguration: 传递注解 @Configuration
    2. @EnableAutoConfiguration:
      1. @AutoConfigurationPackage: 将启动类所在包名包装成 BasePackages, 加入 DI 容器中.
      2. @Import(AutoConfigurationImportSelector.class):
      3. spring.factories 中加载 keyEnableAutoConfiguration 的自动配置类 String 集合.
      4. 对自动配置类 String 集合去重, 排除.
      5. spring.factories 中加载 keyAutoConfigurationImportFilter 的过滤器.
      6. 结合过滤器, 和 META-INF 下的 metadata 中的条件数据, 对自动配置类做一个去重.
      7. 将过滤后的自动配置类交由 Spring 容器执行.
    3. @ScanComponent 基于上一步中得到的 BasePackage, 扫描启动类路径下的所有组件加入容器.