3 SpringBoot 启动流程

Wu Jun 2020-01-13 18:44:31
06 Spring > 01 Core

1 自启动使用实例

1.1 配置 spring.factories

在 resources 下自定义 META-INF/spring.factories,里面填写需要自动化配置的类

org.springframework.boot.autoconfigure.EnableAutoConfiguration = com.sohu.auto.uid.UidAutoConfigure

1.2 实现自定义配置类

UidAutoConfigure

@Configuration
@MapperScan({ "com..." })
@EnableConfigurationProperties(UidProperties.class)
public class UidAutoConfigure {

   @Autowired
   private UidProperties uidProperties;

   @Bean
   DefaultUidGenerator defaultUidGenerator() {
      return new DefaultUidGenerator(uidProperties);
   }
   ...
}

2 源码解析

以 spring-boot 2.2.1 为例

2.1 SpringApplication 启动流程

一个简单 spring-boot-starter-web 项目的入口方法

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

可以在 Spring Boot 容器的启动阶段,扩展:

1)@SpringBootApplication

@SpringBootApplication 注解包含了 @SpringBootConfiguration,@EnableAutoConfiguration 以及 @ComponentScan

2)SpringApplication.run()

实际上是首先创建了 SpringApplication 的实例,然后调用了 SpringApplication 的 run() 方法

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
    return (new SpringApplication(primarySources)).run(args);
}
SpringApplication 实例化
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
    ...
    // 推断应用类型(在类路径中查找类),后面会根据类型初始化对应的环境。常用的一般都是servlet环境。
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 设置初始化器。
    // 初始化 classpath 下 META-INF/spring.factories 中已配置的 Key为 org.springframework.context.ApplicationContextInitializer 的 value
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 设置监听器
    // 初始化 classpath 下 META-INF/spring.factories 中已配置的 Key为 org.springframework.context.ApplicationListener 的 value
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
    ...
}
SpringApplication.run() 方法
public ConfigurableApplicationContext run(String... args) {
    // 记录程序运行时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // 声明 ApplicationContext
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
    // 设置 java.awt.headless 系统属性为 true - 没有图形化界面
    this.configureHeadlessProperty();
    // 第一步:获取并启动监听器
    // 从 META-INF/spring.factories 中获取监听器
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    // 发布事件——“容器开始启动”
    listeners.starting();
    Collection exceptionReporters;
    try {
        // 第二步:构造 ApplicationContext 环境
        // 配置计算机环境,Java 环境,Spring 运行环境,配置Spring 项目 Profiles(SpringBoot 的 application.properties/yml)等等。
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        // 处理需要忽略的 Bean
        this.configureIgnoreBeanInfo(environment);
        // 打印 banner
        Banner printedBanner = this.printBanner(environment);
        // 第三步:初始化 ApplicationContext
        context = this.createApplicationContext();
        // 准备异常报告器
        exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
        // 第四步:ApplicationContext 的前置处理
        this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 第五步:刷新 ApplicationContext
        // IoC 容器的初始化过程。
        // 5.1 Resource 定位(包扫描)
        // 5.2 BeanDefinition 的载入(加载 basePackage,判断 @Component 注解,存在即是 BeanDefinition)
        // 5.3 注册 BeanDefinition(在 IoC 容器中将 BeanDefinition 注入到一个 ConcurrentHashMap 中)
        this.refreshContext(context);
        // 第六步:ApplicationContext 的后置处理
        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, exceptionReporters, listeners);
        throw new IllegalStateException(var10);
    }

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

2.2 SpringBoot 自动装配原理实现

@EnableAutoConfiguration 注解中包含了 @Import(AutoConfigurationImportSelector.class),导入了一个重要的类 AutoConfigurationImportSelector。

// AutoConfigurationImportSelector 类 selectImports 方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        // 加载元数据。(从 META-INF/spring-autoconfigure-metadata.properties 加载)
        AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
        // 获取所有的自动配置类。(从 META-INF/spring.factories 中获取配置的 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的类)
        // 并去掉需要排除的自动装配类。(springboot 的主类上 @SpringBootApplication(exclude = {}) 指定的排除类)
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

2.3 IoC 容器依赖注入

触发依赖注入:

1)获取 bean 名字

bean 获取过程:先获取 bean 名字。会把带有 & 前缀的去掉,或者去 aliasMap 中找这个是不是别名,最终确定 bean 的 id 是什么

2)检查 bean 实例

检查缓存中或者实例工厂中是否有对应的实例,Spring 默认是单例的,如果能获取到直接返回,提高效率。

因为在创建单例 bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring 在创建 bean 的时候不会等 bean 创建完成就会将 bean 的 ObjectFactory 提早曝光,也就是将 ObjectFactory 加入到缓存中。

一旦下一个要创建的 bean 需要依赖上个 bean 则直接使用 ObjectFactory。

3)检查 BeanDefinition

对 IoC 容器中的 BeanDefinition 是否存在进行检查,检查是否能在当前的 BeanFactory 中取得需要的 Bean。

如果当前的工厂中取不到,则到双亲 BeanFactory 中去取。如果当前的双亲工厂取不到,那就顺着双亲 BeanFactory 链一直向上查找。

4)创建 Bean