推荐视频教程download:    (2020新版)基于Spring Cloud微服务架构 广告系统设计与实现  :  http://www.notescloud.top/goods/detail/1253 Spring Boot 源码剖析

Spring Boot依赖管理

问题:(1)为什么导入dependency时不需要指定版本?
Spring Boot项目的父项目依赖spring-boot-starter-parent


<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.1</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

spring-boot-starter-parent中定义了jdk版本,源文件编码方式,Maven打包编译版本等。


<properties>
  <java.version>1.8</java.version>
  <resource.delimiter>@</resource.delimiter>
  <maven.compiler.source>${java.version}</maven.compiler.source>
  <maven.compiler.target>${java.version}</maven.compiler.target>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

并且在build标签中定义了resource资源和pluginManagement


<resources>
  <resource>
    <directory>${basedir}/src/main/resources</directory>
    <filtering>true</filtering>
    <includes>
      <include>**/application*.yml</include>
      <include>**/application*.yaml</include>
      <include>**/application*.properties</include>
    </includes>
  </resource>
  <resource>
    <directory>${basedir}/src/main/resources</directory>
    <excludes>
      <exclude>**/application*.yml</exclude>
      <exclude>**/application*.yaml</exclude>
      <exclude>**/application*.properties</exclude>
    </excludes>
  </resource>
</resources>

里面定义了资源过滤,针对 application 的 yml 、 properties 格式进行了过滤,可以支持不同环境的配置,比如 application-dev.yml 、 application-test.yml 、 application-dev.properties 、 application-dev.properties 等等。

spring-boot-starter-parent项目的父项目是spring-boot-dependencies


<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-dependencies</artifactId>
  <version>2.5.1</version>
</parent>

在这个项目中定义了大量的依赖项目的版本


<properties>
  <activemq.version>5.16.2</activemq.version>
  <antlr2.version>2.7.7</antlr2.version>
  <appengine-sdk.version>1.9.89</appengine-sdk.version>
  <artemis.version>2.17.0</artemis.version>
  <aspectj.version>1.9.6</aspectj.version>
  <assertj.version>3.19.0</assertj.version>
  <atomikos.version>4.0.6</atomikos.version>
  <awaitility.version>4.0.3</awaitility.version>
  <build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version>
  <byte-buddy.version>1.10.22</byte-buddy.version>
......
</properties>

spring-boot-dependencies的dependencyManagement节点
在这里,dependencies定义了SpringBoot版本的依赖的组件以及相应版本。


<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>
    .......
</dependencyManagement>

因为存在这个依赖关系所以在我们创建的Spring Boot项目中部分依赖不需要写版本号。

(2)问题2: spring-boot-starter-parent父依赖启动器的主要作用是进行版本统一管理,那么项目 运行依赖的JAR包是从何而来的?

spring-boot-starter-web
查看spring-boot-starter-web依赖文件源码,核心代码具体如下


<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
  <version>2.5.1</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-json</artifactId>
  <version>2.5.1</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <version>2.5.1</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.3.8</version>
  <scope>compile</scope>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.3.8</version>
  <scope>compile</scope>
</dependency>

可见在spring-boot-starter-web依赖启动器中打包了web开发所需要的底层所有依赖。

因此在引入spring-boot-starter-web依赖启动器时,就可以实现web场景开发,而不需要额外导入tomcat服务器及其他web依赖文件。

Spring Boot除了提供Web依赖启动器之外,还提供了其他很多依赖启动器。具体可以到官网查找。

自动配置

自动配置:根据我们添加的jar包依赖,Spring Boot会将一些配置类的bean注册进ioc容器,我们可以在需要的地方直接使用。

Spring Boot是如何进行自动配置的,都把哪些组件进行了自动配置?

Spring Boot的启动入口是一个被@SpringBootApplication注解的类的main方法。

@SpringBootApplication

查看@SpringBootApplication源码:


//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 标明该类为配置类 
@EnableAutoConfiguration // 启动自动配置功能 
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    // 根据class来排除特定的类,使其不能加入spring容器,传入参数value类型是class类型。 
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    Class<?>[] exclude() default {};

    // 根据classname 来排除特定的类,使其不能加入spring容器,传入参数value类型是class的全 类名字符串数组。 
    @AliasFor(
        annotation = EnableAutoConfiguration.class
    )
    String[] excludeName() default {};

    // 指定扫描包,参数是包名的字符串数组。
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    // 扫描特定的包,参数类似是Class类型数组。
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "nameGenerator"
    )
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

从源码可以看出,@SpringBootApplication是一个组合注解,前面4个是注解的元数据信息,后面三个注解是核心:@SpringBootConfiguration、@EnableAutoConfiguration、
@ComponentScan

@SpringBootConfiguration

查看源码:



@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}

从源码看到,它是一个被@Configuration标记的注解,没有其他的代码。说明它其实就是一个@Configuration的包装,重新命名,功能相同。

被@SpringBootConfiguration 标记的类就是一个配置类。

@EnableAutoConfiguration

查看源码:


//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
// 自动配置包 
@AutoConfigurationPackage
// Spring的底层注解@Import,给容器中导入一个组件; 
// 导入的组件是AutoConfigurationPackages.Registrar.class
@Import({AutoConfigurationImportSelector.class})
// 告诉SpringBoot开启自动配置功能,这样自动配置才能生效。 
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    // 返回不会被导入到 Spring 容器中的类
    Class<?>[] exclude() default {};
    // 返回不会被导入到 Spring 容器中的类名 
    String[] excludeName() default {};
}

@EnableAutoConfiguration是一个组合注解:是@AutoConfigurationPackage 和@Import的组合。


image.png

Spring中很多以Enable开头的注解,其作用就是借助@Import来收集并注册特定场景相关的Bean,并加载到IOC容器。

@EnableAutoConfiguration就是借助@Import来收集所有符合自动配置条件的bean定义,并加载到IoC容器。
它引入了AutoConfigurationImportSelector类。

下面我们继续深入到@AutoConfigurationPackage 和@Import这两个注解中去看看它们做了什么工作。

@AutoConfigurationPackage

查看源码:


//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar;
import org.springframework.context.annotation.Import;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})// 导入Registrar中注册的组件 
public @interface AutoConfigurationPackage {
    String[] basePackages() default {};

    Class<?>[] basePackageClasses() default {};
}

@AutoConfigurationPackage 注解引入Registrar类


static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
    Registrar() {
    }

    /**
     *    注册BeanDefinition
     */
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 根据注解元信息,拿到要扫描的包,扫描并注册到BeanDefinitionRegistry中
        AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
    }

    public Set<Object> determineImports(AnnotationMetadata metadata) {
        return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
    }
}

register方法:


public static void register(BeanDefinitionRegistry registry, String... packageNames) {
    // 第一次进到这个方法的时候private static final String BEAN = AutoConfigurationPackages.class.getName(); 并没有在register中注册。
    if (registry.containsBeanDefinition(BEAN)) {
        AutoConfigurationPackages.BasePackagesBeanDefinition beanDefinition = (AutoConfigurationPackages.BasePackagesBeanDefinition)registry.getBeanDefinition(BEAN);
        beanDefinition.addBasePackages(packageNames);
    } else {
        // 注册BeanDefinition
        registry.registerBeanDefinition(BEAN, new AutoConfigurationPackages.BasePackagesBeanDefinition(packageNames));
    }

}

在register中注册的是AutoConfigurationPackages的内部类BasePackagesBeanDefinition。
BasePackagesBeanDefinition源码:



static final class BasePackagesBeanDefinition extends GenericBeanDefinition {
    private final Set<String> basePackages = new LinkedHashSet();

    BasePackagesBeanDefinition(String... basePackages) {
        // Class对象是BasePackages
        this.setBeanClass(AutoConfigurationPackages.BasePackages.class);
        this.setRole(2);
        // 保存基础包
        this.addBasePackages(basePackages);
    }

    public Supplier<?> getInstanceSupplier() {
        return () -> {
            return new AutoConfigurationPackages.BasePackages(StringUtils.toStringArray(this.basePackages));
        };
    }

    private void addBasePackages(String[] additionalBasePackages) {
        this.basePackages.addAll(Arrays.asList(additionalBasePackages));
    }
}

static final class BasePackages {
    private final List<String> packages;
    private boolean loggedBasePackageInfo;

    BasePackages(String... names) {
        List<String> packages = new ArrayList();
        String[] var3 = names;
        int var4 = names.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String name = var3[var5];
            if (StringUtils.hasText(name)) {
                packages.add(name);
            }
        }

        this.packages = packages;
    }

    List<String> get() {
        if (!this.loggedBasePackageInfo) {
            if (this.packages.isEmpty()) {
                if (AutoConfigurationPackages.logger.isWarnEnabled()) {
                    AutoConfigurationPackages.logger.warn("@EnableAutoConfiguration was declared on a class in the default package. Automatic @Repository and @Entity scanning is not enabled.");
                }
            } else if (AutoConfigurationPackages.logger.isDebugEnabled()) {
                String packageNames = StringUtils.collectionToCommaDelimitedString(this.packages);
                AutoConfigurationPackages.logger.debug("@EnableAutoConfiguration was declared on a class in the package '" + packageNames + "'. Automatic @Repository and @Entity scanning is enabled.");
            }

            this.loggedBasePackageInfo = true;
        }

        return this.packages;
    }
}

BasePackagesBeanDefinition 继承了GenericBeanDefinition,说明它就是一个Spring的BeanDefinition。只是在这个BeanDefinition中保存的Class并不是AutoConfigurationPackages,而是
BasePackages。

因此@EnableAutoConfiguration中的@AutoConfigurationPackage使用@Import({Registrar.class})注解,向Ioc容器中注册了一个BasePackages的Bean定义。

用图来描述一下整个过程:


image.png

@Import({AutoConfigurationImportSelector.class})

@Import({AutoConfigurationImportSelector.class}) :将
AutoConfigurationImportSelector 这个类导入到 Spring 容器中,
AutoConfigurationImportSelector 可以帮助 Springboot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IOC 容器( ApplicationContext )中。


image

可以看到 AutoConfigurationImportSelector 重点是实现了 DeferredImportSelector 接口和各种 Aware 接口,然后 DeferredImportSelector 接口又继承了 ImportSelector 接口。
其不光实现了 ImportSelector 接口,还实现了很多其它的 Aware 接口,分别表示在某个时机会被回调。

自动配置实现逻辑的入口方法:

AutoConfigurationImportSelector将会被放到DeferredImportSelectorGrouping中的deferredImports集合中。并且在DeferredImportSelectorGrouping的getImports()方法中进行统一处理。

跟自动配置逻辑相关的入口方法在 DeferredImportSelectorGrouping 类的 getImports 方法处.


public Iterable<Entry> getImports() {
    Iterator var1 = this.deferredImports.iterator();
    // 遍历DeferredImportSelectorGrouping的deferredImports集合。AutoConfigurationImportSelector也在这个集合中。
    while(var1.hasNext()) {
        ConfigurationClassParser.DeferredImportSelectorHolder deferredImport = (ConfigurationClassParser.DeferredImportSelectorHolder)var1.next();
        
        // (1)利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定导入哪些配置类
        this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector());
    }
    // (2)经过上面的处理后,然后再进行选择导入哪些配置类
    return this.group.selectImports();
}

其中重要的两个步骤:

  • 利用AutoConfigurationGroup的process方法来处理自动配置的相关逻辑,决定导入哪些配置类
  • 经过上面的处理后,然后再进行选择导入哪些配置类
分析自动配置的主要逻辑

AutoConfigurationImportSelector的process方法:


// 这里用来处理自动配置类,比如过滤掉不符合匹配条件的自动配置类 
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
        return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
    });
    // (1)调用getAutoConfigurationEntry方法得到自动配置类放入 autoConfigurationEntry对象中 
    AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
    // (2)又将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合 
    this.autoConfigurationEntries.add(autoConfigurationEntry);
    Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();
    // (3)遍历刚获取的自动配置类,放入entries
    while(var4.hasNext()) {
        String importClassName = (String)var4.next();
        // 这里符合条件的自动配置类作为key,annotationMetadata作为值放进entries集合
        this.entries.putIfAbsent(importClassName, annotationMetadata);
    }

}

其中重要的三个步骤:

  • 调用getAutoConfigurationEntry方法得到自动配置类放入 autoConfigurationEntry对象中
  • 又将封装了自动配置类的autoConfigurationEntry对象装进autoConfigurationEntries集合
  • 遍历刚获取的自动配置类,放入entries

获取自动配置类方法getAutoConfigurationEntry:


protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    } else {
        AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
        // (1)得到spring.factories文件配置的所有自动配置类
        List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
        // 利用LinkedHashSet移除重复的配置类 
        configurations = this.removeDuplicates(configurations);
        // 得到要排除的自动配置类,比如注解属性exclude的配置类 
        // 比如:@SpringBootApplication(exclude = FreeMarkerAutoConfiguration.class) 
        // 将会获取到exclude = FreeMarkerAutoConfiguration.class的注解数据
        Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
        // 检查要被排除的配置类,因为有些不是自动配置类,故要抛出异常
        this.checkExcludedClasses(configurations, exclusions);
        // (2)将要排除的配置类移除 
        configurations.removeAll(exclusions);
        // (3)对加载到的自动配置类按需要进行过滤
        configurations = this.getConfigurationClassFilter().filter(configurations);
        // (4)获取了符合条件的自动配置类后,此时触发AutoConfigurationImportEvent事件 
        // 目的是告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类 
        // 该事件什么时候会被触发?--> 在刷新容器时调用invokeBeanFactoryPostProcessors后置处理器时触发
        this.fireAutoConfigurationImportEvents(configurations, exclusions);
        // (5)将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回 
        return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
    }
}

其中重要的五个步骤:

  • 得到spring.factories文件配置的所有自动配置类
  • 将要排除的配置类移除
  • 对加载到的自动配置类按需要进行过滤
  • 告诉ConditionEvaluationReport条件评估报告器对象来记录符合条件的自动配置类
  • 将符合条件和要排除的自动配置类封装进AutoConfigurationEntry对象,并返回

getCandidateConfigurations获取配置文件中的自动配置类过程分析
这个方法中重要的方法是loadFactoryNames,这个方法的作用是加载一些组件的名字。


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

进入loadFactoryNames方法,它只是做了简单验证,就委托另外一个方法loadSpringFactories 去加载组件名。



public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    ClassLoader classLoaderToUse = classLoader;
    if (classLoader == null) {
        classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
    }

    String factoryTypeName = factoryType.getName();
    // 真正去加载组件名的方法
    return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}

继续进入loadSpringFactories方法:


private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
    Map<String, List<String>> result = (Map)cache.get(classLoader);
    if (result != null) {
        return result;
    } else {
        HashMap result = new HashMap();

        try {
        
            // 加载类路径下spring.factories文件,将其中设置的配置类的全路径信息封装 为Enumeration类对象
            Enumeration urls = classLoader.getResources("META-INF/spring.factories");

            //循环Enumeration类对象,根据相应的节点信息生成Properties对象,通过传入 的键获取值,在将值切割为一个个小的字符串转化为Array,方法result集合中
            while(urls.hasMoreElements()) {
                URL url = (URL)urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                Iterator var6 = properties.entrySet().iterator();

                while(var6.hasNext()) {
                    Entry<?, ?> entry = (Entry)var6.next();
                    String factoryTypeName = ((String)entry.getKey()).trim();
                    String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                    String[] var10 = factoryImplementationNames;
                    int var11 = factoryImplementationNames.length;

                    for(int var12 = 0; var12 < var11; ++var12) {
                        String factoryImplementationName = var10[var12];
                        ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                            return new ArrayList();
                        })).add(factoryImplementationName.trim());
                    }
                }
            }

            result.replaceAll((factoryType, implementations) -> {
                return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
            });
            cache.put(classLoader, result);
            return result;
        } catch (IOException var14) {
            throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
        }
    }
}

这个方法的主要作用是遍历整个ClassLoader下所有的jar包下的Spring.factories文件。


image

而在spring-boot-autoconfigure包的META-INF下的spring.factories中保存着springboot的默认提供的自动配置类。

image

我们下面总结下 getAutoConfigurationEntry 方法主要做的事情:
【1】从 spring.factories 配置文件中加载 EnableAutoConfiguration 自动配置类),获取的自动配
置类如图所示。
【2】若 @EnableAutoConfiguration 等注解标有要 exclude 的自动配置类,那么再将这个自动配置类排除掉;
【3】排除掉要 exclude 的自动配置类后,然后再调用 filter 方法进行进一步的过滤,再次排除一些
不符合条件的自动配置类;
【4】经过重重过滤后,此时再触发 AutoConfigurationImportEvent 事件,告诉
ConditionEvaluationReport 条件评估报告器对象来记录符合条件的自动配置类;
【5】 最后再将符合条件的自动配置类返回。

总结了 getAutoConfigurationEntry 方法主要的逻辑后,我们再来细看一下
AutoConfigurationImportSelector 的 filter 方法:


List<String> filter(List<String> configurations) {
    long startTime = System.nanoTime();
    
    // 将从spring.factories中获取的自动配置类转出字符串数组 
    String[] candidates = StringUtils.toStringArray(configurations);
    boolean skipped = false;
    Iterator var6 = this.filters.iterator();

    int i;
    while(var6.hasNext()) {
        AutoConfigurationImportFilter filter = (AutoConfigurationImportFilter)var6.next();
        
        // 判断各种filter来判断每个candidate(这里实质要通过candidate(自动配置类)拿到其标注的 
// @ConditionalOnClass,@ConditionalOnBean和@ConditionalOnWebApplication里面的注解值)是否匹配, 
        boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);

        // 遍历match数组,注意match顺序跟candidates的自动配置类一一对应
        for(i = 0; i < match.length; ++i) {
            // 若有不匹配的话 
            if (!match[i]) {
                candidates[i] = null;
                // 标注skipped为true 
                skipped = true;
            }
        }
    }

    // 这里表示若所有自动配置类经过OnBeanCondition,OnClassCondition和 OnWebApplicationCondition过滤后,全部都匹配的话,则全部原样返回
    if (!skipped) {
        return configurations;
    } else {
        // 建立result集合来装匹配的自动配置类 
        List<String> result = new ArrayList(candidates.length);
        String[] var12 = candidates;
        int var14 = candidates.length;

        for(i = 0; i < var14; ++i) {
            String candidate = var12[i];
            // 符合条件的自动配置类,此时添加到result集合中 
            if (candidate != null) {
                result.add(candidate);
            }
        }

        if (AutoConfigurationImportSelector.logger.isTraceEnabled()) {
            int numberFiltered = configurations.size() - result.size();
            AutoConfigurationImportSelector.logger.trace("Filtered " + numberFiltered + " auto configuration class in " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
        }
        // 最后返回符合条件的自动配置类
        return result;
    }
}

AutoConfigurationImportSelector 的 filter 方法主要做的事情就是调用AutoConfigurationImportFilter 接口的match 方法来判断每一个自动配置类上的条件注解(若有的话) @ConditionalOnClass ,@ConditionalOnBean 或 @ConditionalOnWebApplication 是否满足条件,若满足,则返回true,说明匹配,若不满足,则返回false说明不匹配。

我们现在知道 AutoConfigurationImportSelector 的 filter 方法主要做了什么事情就行了,现在先不用研究的过深

有选择的导入自动配置类
this.group.selectImports 方法是如何进一步有选择的导入自动配置类的。


public Iterable<Entry> selectImports() {
    if (this.autoConfigurationEntries.isEmpty()) {
        return Collections.emptyList();
    } else {
        // 这里得到所有要排除的自动配置类的set集合
        Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
        // 这里得到经过滤后所有符合条件的自动配置类的set集合
        Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationImportSelector.AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
        
        // 移除掉要排除的自动配置类 
        processedConfigurations.removeAll(allExclusions);
        // 对标注有@Order注解的自动配置类进行排序
        return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
            return new Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
        }).collect(Collectors.toList());
    }
}

selectImports 方法的自动配置类再进一步排除 exclude 的自动配置类,然后再排序
最后,我们再总结下SpringBoot自动配置的原理,主要做了以下事情:

  1. 从spring.factories配置文件中加载自动配置类;
  2. 加载的自动配置类中排除掉 @EnableAutoConfiguration 注解的 exclude 属性指定的自动配置类;
  3. 然后再用 AutoConfigurationImportFilter 接口去过滤自动配置类是否符合其标注注解(若有标注的话) @ConditionalOnClass , @ConditionalOnBean 和 @ConditionalOnWebApplication 的条件,若都符合的话则返回匹配结果;
  4. 然后触发 AutoConfigurationImportEvent 事件,告诉 ConditionEvaluationReport 条件评估报告器对象来分别记录符合条件和 exclude 的自动配置类。
  5. 最后spring再将最后筛选后的自动配置类导入IOC容器中

画图梳理一下这个流程


image.png

总结:

  • 在当前Spring Boot工程中,各个组件(jar包),都会存在一个名称为META-INF的目录,目录中有一个sping.factories文件。
  • 在这个文件中会配置工厂类的全路径如MyBatis中配置了org.mybatis.spring.boot.autoconfigure.MyBatisAutoConfiguration。
  • Spring Boot 通过@EnableAutoConfiguration注解收集配置工厂类
  • 由Spring创建bean实例存到IoC容器中。


image

条件注解

@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

  • @ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
  • @ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。
  • @ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。基于SpEL表达式 的条件判断。
  • @ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。
  • @ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean。
  • @ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。
  • @ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
  • @ConditionalOnNotWebApplication:当项目不是一个Web项目时进行实例化。
  • @ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
  • @ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
  • @ConditionalOnResource:当类路径下有指定的资源时触发实例化。
  • @ConditionalOnJndi:在JNDI存在的条件下触发实例化。
  • @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定了首 选的Bean时触发实例化。

@ComponentScan

  • @ConponentScan用于配置扫描Spring的扫包基础路径的注解。默认扫描被这个注解标记的类的目录及子目录。
  • @SpringBootApplication中包含了@ConponentScan,因此它具备@ConponentScan相同的功能,因此被@SpringBootApplication注解标记了的类的类路径及其子路径上的bean都能被扫描到。这也是为什么非这个目录及其子目录中的bean不能被扫描的原因。

SpringApplication初始化过程

@SpringBootApplication中的@ComponentScan是在什么时候被扫描到的?
SpringBoot项目的main方法:



@SpringBootApplication
@EnableConfigurationProperties
public class SpringBootDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootDemoApplication.class, args);
    }

}

从run方法开始追踪:


public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class[]{primarySource}, args);
}

调用了另外一个run方法:


public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}

这个run方法构建了一个SpringApplication类的实例,我们继续来看下SpringApplication类的构造函数

SpringApplication构造函数


public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.sources = new LinkedHashSet();
    this.bannerMode = Mode.CONSOLE;
    this.logStartupInfo = true;
    this.addCommandLineProperties = true;
    this.addConversionService = true;
    this.headless = true;
    this.registerShutdownHook = true;
    this.additionalProfiles = Collections.emptySet();
    this.isCustomEnvironment = false;
    this.lazyInitialization = false;
    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
    this.applicationStartup = ApplicationStartup.DEFAULT;
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    // 1、推断应用类型:servlet、reactive、none
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = this.getBootstrapRegistryInitializersFromSpringFactories();
    // 2、初始化META-INF/spring.factories文件中配置的ApplicationContextInitializer
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 3、初始化classpath下的所有已配置的ApplicationListener
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    // 4、推断出main方法的类名。
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

判断应用类型

private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};

static WebApplicationType deduceFromClasspath() {
    if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
        return REACTIVE;
    } else {
        String[] var0 = SERVLET_INDICATOR_CLASSES;
        int var1 = var0.length;

        for(int var2 = 0; var2 < var1; ++var2) {
            String className = var0[var2];
            if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                return NONE;
            }
        }

        return SERVLET;
    }
}

判断应用类型主要分三个步骤:

  • 1、判断类路径中有没有DispatcherHandler,DispatcherServlet,ServletContainer:如果有DispatcherHandler,没有DispatcherServlet,ServletContainer则是REACTIVE(响应式编程)类型的应用。
  • 2、如果不满足条件1,如果类路径不包含Servlet类或不包含ConfigurableWebApplicationContext类,则是NONE(不是一个web应用)类型应用。
  • 如果条件1、2都不满足,那它就是一个Servlet(web应用,需要启动内置servlet容器)应用。
初始化ApplicationContextInitializer

this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));


private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = this.getClassLoader();
    // 从MATA-INF/spring.factories文件中读取所有ApplicationContextInitializer类全路径为key的所有类全路径
    Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    // 反射实例化这些ApplicationContextInitializer类
    List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

主要完成了两步操作:

  • 从MATA-INF/spring.factories文件中读取所有ApplicationContextInitializer类全路径为key的所有类全路径。
  • 反射实例化上一步获取到的类,并放入到一个集合中。
初始化classpath下的所有ApplicationListener
`
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
``
加载过程跟初始化器一样
主要完成了两步操作:
- 从MATA-INF/spring.factories文件中读取所有ApplicationListener类全路径为key的所有类全路径。
- 反射实例化上一步获取到的类,并放入到一个集合中。

#### 根据调用栈,推断出main方法的类

java this.mainApplicationClass = this.deduceMainApplicationClass();

`


private Class<?> deduceMainApplicationClass() { try { StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace(); StackTraceElement[] var2 = stackTrace; int var3 = stackTrace.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        StackTraceElement stackTraceElement = var2[var4];
        if ("main".equals(stackTraceElement.getMethodName())) {
            return Class.forName(stackTraceElement.getClassName());
        }
    }
} catch (ClassNotFoundException var6) {
}

return null;

}



这一步的主要功能是:

* 根据调用栈,找到main方法所在类的class对象,存储到成员变量。

SpringApplication.run方法流程介绍
---------------------------

SpringApplication对象构建完后调用run方法,run方法代码如下:  


public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); DefaultBootstrapContext bootstrapContext = this.createBootstrapContext(); ConfigurableApplicationContext context = null; this.configureHeadlessProperty(); //从META-INF/spring.factories中获取监听器 //1、获取并启动监听器 SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass);

try {
    ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    //2、构造应用上下文环境
    ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
    this.configureIgnoreBeanInfo(environment);
    Banner printedBanner = this.printBanner(environment);
    //3、初始化应用上下文
    context = this.createApplicationContext();
    context.setApplicationStartup(this.applicationStartup);
    //4、刷新应用上下文前的准备阶段 
    this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
    //5、刷新应用上下文
    this.refreshContext(context);
    //6、刷新应用上下文后的扩展接口
    this.afterRefresh(context, applicationArguments);
    stopWatch.stop();
    if (this.logStartupInfo) {
        (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
    }

    listeners.started(context);
    this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
    this.handleRunFailure(context, var10, listeners);
    throw new IllegalStateException(var10);
}

try {
    listeners.running(context);
    return context;
} catch (Throwable var9) {
    this.handleRunFailure(context, var9, (SpringApplicationRunListeners)null);
    throw new IllegalStateException(var9);
}

}



在以上的代码中,启动过程中的重要步骤共分为六步 :

* 1、获取并启动监听器
* 2、构造应用上下文环境
* 3、初始化应用上下文
* 4、刷新应用上下文前的准备阶段
* 5、刷新应用上下文
* 6、刷新应用上下文后的扩展接口

### 获取并启动监听器

事件机制在Spring是很重要的一部分内容,通过事件机制我们可以监听Spring容器中正在发生的一些事件,同样也可以自定义监听事件。Spring的事件为Bean和Bean之间的消息传递提供支持。当一个对象处理完某种任务后,通知另外的对象进行某些处理,常用的场景有进行某些操作后发送通知,消息、邮件等情况。

获取监听器的入口代码:  


SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass);



进入到方法中:  


private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, // 从META-INF/spring.factories文件中获取监听器 getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup); }



从代码中看到,在构建SpringApplicationRunListeners实例之前调用了一个我们非常熟悉的方法:getSpringFactoriesInstances,该方法的主要作用是从spring.factories文件中加载SpringApplicationRunListener类全路径为key的所有类,并实例化。  


private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates // 根据传入的type类型,使用type的类名全路径作为key,到META-INF/spring.factories文件中去查找值,并放到names中 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 将查找到的类通过反射实例化 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 按order注解顺序排序 AnnotationAwareOrderComparator.sort(instances); return instances; }



获取到监听器后,启动监听器:  


listeners.starting(bootstrapContext, this.mainApplicationClass);



### 构造应用上下文环境

用上下文环境包括什么呢?包括计算机的环境,Java环境,Spring的运行环境,Spring项目的配置(在SpringBoot中就是那个熟悉的application.properties/yml)等等。  


private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { //创建并配置相应的环境 ConfigurableEnvironment environment = this.getOrCreateEnvironment(); //根据用户配置,配置 environment系统环境 this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs()); // 启动相应的监听器,其中一个重要的监听器 ConfigFileApplicationListener 就是加载项目配置文件的监听器。 ConfigurationPropertySources.attach((Environment)environment); listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment); DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment); Assert.state(!((ConfigurableEnvironment)environment).containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); this.bindToSpringApplication((ConfigurableEnvironment)environment); if (!this.isCustomEnvironment) { environment = (new EnvironmentConverter(this.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass()); }

ConfigurationPropertySources.attach((Environment)environment);
return (ConfigurableEnvironment)environment;

}



getOrCreateEnvironment方法做了什么?  


private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } switch (this.webApplicationType) { case SERVLET: // 如果应用类型是SERVLET,则创建Servlet类型的环境 return new ApplicationServletEnvironment(); case REACTIVE: // 如果应用类型是响应式编程的,则创建Reactive类型的环境 return new ApplicationReactiveWebEnvironment(); default: return new ApplicationEnvironment(); } }



ApplicationServletEnvironment类型的继承关系:

![](https://s2.51cto.com/images/20210905/1630839454674380.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)  
image  


/**

  • Template method delegating to
  • {@link #configurePropertySources(ConfigurableEnvironment, String[])} and
  • {@link #configureProfiles(ConfigurableEnvironment, String[])} in that order.
  • Override this method for complete control over Environment customization, or one of
  • the above for fine-grained control over property sources or profiles, respectively.
  • @param environment this application's environment
  • @param args arguments passed to the {@code run} method
  • @see #configureProfiles(ConfigurableEnvironment, String[])
  • @see #configurePropertySources(ConfigurableEnvironment, String[]) */ protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) { if (this.addConversionService) { environment.setConversionService(new ApplicationConversionService()); } // 将main方法的参数分组到SimpleCommandLinePropertySource中 configurePropertySources(environment, args); // 激活相关的配置文件 configureProfiles(environment, args); }


### 初始化应用上下文


/** *用于创建 {@link ApplicationContext} 的策略方法

  • Strategy method used to create the {@link ApplicationContext}. By default this
  • method will respect any explicitly set application context class or factory before
  • falling back to a suitable default.
  • @return the application context (not yet refreshed)
  • @see #setApplicationContextClass(Class)
  • @see #setApplicationContextFactory(ApplicationContextFactory) */ protected ConfigurableApplicationContext createApplicationContext() { // 根据应用类型创建对应的 ApplicationContext实例 return this.applicationContextFactory.create(this.webApplicationType); }


调试进入:
org.springframework.boot.ApplicationContextFactory  


ApplicationContextFactory DEFAULT = (webApplicationType) -> { try { switch (webApplicationType) { case SERVLET: // 应用类型是Servlet时,new一个AnnotationConfigServletWebServerApplicationContext上下文实例对象 return new AnnotationConfigServletWebServerApplicationContext(); case REACTIVE: return new AnnotationConfigReactiveWebServerApplicationContext(); default: return new AnnotationConfigApplicationContext(); } } catch (Exception ex) { throw new IllegalStateException("Unable create a default ApplicationContext instance, " + "you may need a custom ApplicationContextFactory", ex); } };



AnnotationConfigServletWebServerApplicationContext的类继承关系:

![](https://s2.51cto.com/images/20210905/1630839454388137.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)  
image

GenericApplicationContext是AnnotationConfigServletWebServerApplicationContext的父类。

创建对象的时候,创建了一个reader和一个scanner:  


/**

  • Create a new {@link AnnotationConfigServletWebServerApplicationContext} that needs
  • to be populated through {@link #register} calls and then manually
  • {@linkplain #refresh refreshed}. */ public AnnotationConfigServletWebServerApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); }


另外在调用AnnotationConfigServletWebServerApplicationContext构造函数之前,要先调用父类的构造函数:  


public GenericApplicationContext() { this.customClassLoader = false; this.refreshed = new AtomicBoolean(); this.beanFactory = new DefaultListableBeanFactory(); }



AnnotationConfigServletWebServerApplicationContext中的IoC容器在其父类的构造方法中被实例化。  
![](https://s2.51cto.com/images/20210905/1630839454640331.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)  
image

### 刷新应用上下文前的准备阶段

刷新应用上下文前的准备阶段,就是想上下文context中设置一些属性,完成bean对象的创建。  


private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 设置容器环境 context.setEnvironment(environment); // 执行容器后置处理器,向context中设置值 postProcessApplicationContext(context); // 遍历初始化器,执行初始化 applyInitializers(context); // 向各个监听器去发送容器准备好的事件 listeners.contextPrepared(context); bootstrapContext.close(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources // 获取所有启动类 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }



Set\<Object\> sources = getAllSources();拿到启动类,我们来看下它是怎么拿到的?  


public Set<Object> getAllSources() { Set<Object> allSources = new LinkedHashSet<>(); // primarySources在SpringApplication的run方法中传入的 if (!CollectionUtils.isEmpty(this.primarySources)) { allSources.addAll(this.primarySources); } if (!CollectionUtils.isEmpty(this.sources)) { allSources.addAll(this.sources); } return Collections.unmodifiableSet(allSources); }



从源码中看到:它是将primarySources和sources两个集合合并后返回。因此primarySources和sources中存放的就是启动类。  
下面看看它们在哪里被赋值的:  
在SpringApplication的构造方法中,将构造方法中的参数直接添加到了primarySources集合中。  


public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }



另外有一个方法也涉及到往这个集合中添加值:  


public void addPrimarySources(Collection<Class<?>> additionalPrimarySources) { this.primarySources.addAll(additionalPrimarySources); }



但是通过findUseage发现,在启动流程中,这个方法还没有被调用过。因此,当前状态下primarySources集合中只有SpringApplication.run(SpringBootDemoApplication.class, args);  
参数中传入的启动类。

另外一个集合sources的只在一个方法中赋值:  


public void setSources(Set<String> sources) { Assert.notNull(sources, "Sources must not be null"); this.sources = new LinkedHashSet<>(sources); }



这个方法只被SampleSpringXmlApplication类中的main方法调用,而Spring Boot的启动类是通过SpringApplication的run方法启动的。因此sources集合是空的。
> Set\<Object\> sources = getAllSources();在Spring Boot工程启动过程中,拿到的是SpringBoot启动类,就是传入到SpringApplication.run()方法的第一个参数。

验证:  
断点打在run方法

![](https://s2.51cto.com/images/20210905/1630839454256014.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)  
image

primarySources 集合中只有一个SpringBootDemoApplication类对象

![](https://s2.51cto.com/images/20210905/1630839454755073.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)  
image

这个类对象正好是我们的启动类

![](https://s2.51cto.com/images/20210905/1630839455748804.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)  
image

断点打到getAllResource

![](https://s2.51cto.com/images/20210905/1630839455971201.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)  
image

allSources中只有我们的启动类对象,sources集合为空,验证了我们的结论。  
![](https://s2.51cto.com/images/20210905/1630839455570183.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)  
image

**接着看load()方法**  


/**

  • Load beans into the application context.

  • @param context the context to load beans into

  • @param sources the sources to load */ protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); }

    // 创建BeanDefinitionLoader BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } loader.load(); }



在创建BeanDefinitionLoader之前,先获取BeanDefinitionRegistery:  


private BeanDefinitionRegistry getBeanDefinitionRegistry(ApplicationContext context) { if (context instanceof BeanDefinitionRegistry) { return (BeanDefinitionRegistry) context; } if (context instanceof AbstractApplicationContext) { return (BeanDefinitionRegistry) ((AbstractApplicationContext) context).getBeanFactory(); } throw new IllegalStateException("Could not locate BeanDefinitionRegistry"); }



其实就是将context中的IOC容器直接拿出来,并且转成BeanDefinitionRegistry。在前面看AnnotationConfigServletWebApplicationContext类继承关系的时候知道,它实现了BeanDefinitionRegistry接口,因此可以强转。并且可以验证这里就是AnnotationConfigServletWebApplicationContext。

![](https://s2.51cto.com/images/20210905/1630839455510039.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_30,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)  
image

BeanDefinitionRegistry定义了很重要的方法registerBeanDefinition(),该方法将BeanDefinition 注册进DefaultListableBeanFactory容器的beanDefinitionMap中。

现在再来看创建BeanDefinitionLoader的过程:  


protected BeanDefinitionLoader createBeanDefinitionLoader(BeanDefinitionRegistry registry, Object[] sources) { return new BeanDefinitionLoader(registry, sources); }



构造方法:  


/**

  • Create a new {@link BeanDefinitionLoader} that will load beans into the specified
  • {@link BeanDefinitionRegistry}.
  • @param registry the bean definition registry that will contain the loaded beans
  • @param sources the bean sources */ BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { Assert.notNull(registry, "Registry must not be null"); Assert.notEmpty(sources, "Sources must not be empty"); this.sources = sources; //注解形式的Bean定义读取器 比如:@Configuration @Bean @Component @Controller @Service等等 this.annotatedReader = new AnnotatedBeanDefinitionReader(registry); //XML形式的Bean定义读取器 this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null); this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null); //类路径扫描器 this.scanner = new ClassPathBeanDefinitionScanner(registry); //扫描器添加排除过滤器 this.scanner.addExcludeFilter(new ClassExcludeFilter(sources)); }


在该方法中创建了reader和scanner,这两个很熟悉的东西,后面肯定会使用reader去读取配置,scaner进行包扫描。

接着看loader.load();方法:  


/**

  • Load the sources into the reader. */ void load() { // 遍历启动类,分别进行load for (Object source : this.sources) { load(source); } }

private void load(Object source) { Assert.notNull(source, "Source must not be null"); // 从calss加载 if (source instanceof Class<?>) { load((Class<?>) source); return; } // 从resource加载 if (source instanceof Resource) { load((Resource) source); return; } // 从package加载 if (source instanceof Package) { load((Package) source); return; } // 从CharSequence加载 if (source instanceof CharSequence) { load((CharSequence) source); return; } throw new IllegalArgumentException("Invalid source type " + source.getClass()); }



Spring Boot应用启动时会从class加载。  


private void load(Class<?> source) { if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) { // Any GroovyLoaders added in beans{} DSL can contribute beans here GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class); ((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans()); }

// 判断启动类是否符合注册条件 if (isEligible(source)) { this.annotatedReader.register(source); } }



什么是符合注册条件的:  


private boolean isEligible(Class<?> type) { // 不是匿名的,就是没有名字的类。不是groovy闭包。不是没有构造方法的 return !(type.isAnonymousClass() || isGroovyClosure(type) || hasNoConstructors(type)); }



这里,启动类是符合注册条件的。  
所以启动类会被注册到IoC容器中。  


this.annotatedReader.register(source);



而annotatedReader是BeanDefinitionLoader构造函数中被实例化的,并且它是Spring Framework中的类,所以注册过程是由Spring Framework完成的  


BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { Assert.notNull(registry, "Registry must not be null"); Assert.notEmpty(sources, "Sources must not be empty"); this.sources = sources; // AnnotatedBeanDefinitionReader是Spring Framework中的组件 this.annotatedReader = new AnnotatedBeanDefinitionReader(registry); this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null); this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null); this.scanner = new ClassPathBeanDefinitionScanner(registry); this.scanner.addExcludeFilter(new ClassExcludeFilter(sources)); }



### 刷新应用上下文

刷新上下文代码:  


private void refreshContext(ConfigurableApplicationContext context) { if (this.registerShutdownHook) { shutdownHook.registerApplicationContext(context); } // 核心方法 refresh(context); }



从源码可以看到,刷新上下文的核心方法是refresh方法  


/**

  • Refresh the underlying {@link ApplicationContext}.
  • @param applicationContext the application context to refresh */ protected void refresh(ConfigurableApplicationContext applicationContext) { applicationContext.refresh(); }


直接直接调用了application的refresh方法  


public void refresh() throws BeansException, IllegalStateException { synchronized(this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // 第⼀步:刷新前的预处理 this.prepareRefresh(); /* 第⼆步: 获取BeanFactory;默认实现是DefaultListableBeanFactory 加载BeanDefition 并注册到 BeanDefitionRegistry */ ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory(); // 第三步:BeanFactory的预准备⼯作(BeanFactory进⾏⼀些设置,⽐如context的类加载器等) this.prepareBeanFactory(beanFactory);

    try {
        // 第四步:BeanFactory准备⼯作完成后进⾏的后置处理⼯作
        this.postProcessBeanFactory(beanFactory);
        StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
        // 第五步:实例化并调⽤实现了BeanFactoryPostProcessor接⼝的Bean
        this.invokeBeanFactoryPostProcessors(beanFactory);
        // 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执⾏
        this.registerBeanPostProcessors(beanFactory);
        beanPostProcess.end();
        // 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
        this.initMessageSource();
        // 第⼋步:初始化事件派发器
        this.initApplicationEventMulticaster();
        // 第九步:⼦类重写这个⽅法,在容器刷新的时候可以⾃定义逻辑
        this.onRefresh();
        // 第⼗步:注册应⽤的监听器。就是注册实现了ApplicationListener接⼝的监听器bean
        this.registerListeners();
        /* 
         第⼗⼀步: 
         初始化所有剩下的⾮懒加载的单例bean 
         初始化创建⾮懒加载⽅式的单例Bean实例(未设置属性) 
         填充属性 
         初始化⽅法调⽤(⽐如调⽤afterPropertiesSet⽅法、init-method⽅法) 
         调⽤BeanPostProcessor(后置处理器)对实例bean进⾏后置处 
         */
        this.finishBeanFactoryInitialization(beanFactory);
        /* 
         第⼗⼆步: 
         完成context的刷新。主要是调⽤LifecycleProcessor的onRefresh()⽅法,并且发布事 
        件 (ContextRefreshedEvent) 
         */
        this.finishRefresh();
    } catch (BeansException var10) {
        if (this.logger.isWarnEnabled()) {
            this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
        }

        this.destroyBeans();
        this.cancelRefresh(var10);
        throw var10;
    } finally {
        this.resetCommonCaches();
        contextRefresh.end();
    }

}

}



**invokeBeanFactoryPostProcessors(beanFactory);(重点)**   
IoC容器的初始化过程包括三个步骤,在invokeBeanFactoryPostProcessors()方法中完成了IoC容  
器初始化过程的三个步骤。  
**1,第一步:Resource定位**   
在SpringBoot中,我们都知道他的包扫描是从主类所在的包开始扫描的,prepareContext()  
方法中,会先将主类解析成BeanDefinition,然后在refresh()方法的  
invokeBeanFactoryPostProcessors()方法中解析主类的BeanDefinition获取basePackage的路  
径。这样就完成了定位的过程。其次SpringBoot的各种starter是通过SPI扩展机制实现的自动装  
配,SpringBoot的自动装配同样也是在invokeBeanFactoryPostProcessors()方法中实现的。还有  
一种情况,在SpringBoot中有很多的@EnableXXX注解,细心点进去看的应该就知道其底层是  
@Import注解,在invokeBeanFactoryPostProcessors()方法中也实现了对该注解指定的配置类的  
定位加载。  
常规的在SpringBoot中有三种实现定位,第一个是主类所在包的,第二个是SPI扩展机制实现  
的自动装配(比如各种starter),第三种就是@Import注解指定的类。(对于非常规的不说了)

**2,第二步:BeanDefinition的载入**   
在第一步中说了三种Resource的定位情况,定位后紧接着就是BeanDefinition的分别载入。  
所谓的载入就是通过上面的定位得到的basePackage,SpringBoot会将该路径拼接成:  
classpath:com/lagou/\*\*/.class这样的形式,然后一个叫做  
xPathMatchingResourcePatternResolver的类会将该路径下所有的.class文件都加载进来,然后  
遍历判断是不是有@Component注解,如果有的话,就是我们要装载的BeanDefinition。大致过  
程就是这样的了。

**3、第三个过程:注册BeanDefinition**   
这个过程通过调用上文提到的BeanDefinitionRegister接口的实现来完成。这个注册过程把载入  
过程中解析得到的BeanDefinition向IoC容器进行注册。通过上文的分析,我们可以看到,在IoC容  
器中将BeanDefinition注入到一个ConcurrentHashMap中,IoC容器就是通过这个HashMap来持  
有这些BeanDefinition数据的。比如DefaultListableBeanFactory 中的beanDefinitionMap属性。  
OK,总结完了,接下来我们通过代码看看具体是怎么实现的。

### 刷新应用上下文后的扩展接口


/**

  • Called after the context has been refreshed.
  • @param context the application context
  • @param args the application arguments */ protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { }


扩展接口,设计模式中的模板方法,默认为空实现。如果有自定义需求,可以重写该方法。比如打印一些启动结束log,或者一些其它后置处理。