SpringBoot启动流程

首先 Springboot 框架总体来说还是基于 Spring 进行的优化和加强!

所以其总体核心还是没有变化 也就是 ApplicationContext Ioc容器

而SpringBoot 相较于 Spring 的特点和核心是autoconfiguration和springboot 中的 ==spring.factories==

该文件是实现springboot自动装配的重要部分

1、程序入口

首先会有一个程序的入口,进行开启

@SpringBootApplication
public class SpringTestApplication {

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

}
  • 程序入口要被@SpringBootApplication给标注

  • @SpringBootApplication 点进去可以看见有三个重要注解

    • @SpringBootConfiguration //声明该类为SpringBoot配置类
      @EnableAutoConfiguration //开启配置的自动装配
      @ComponentScan //开启自动扫描注解
      

2、启动初始化器

他会执行如下命令

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
  	...
    //资源加载器默认根据前面判断,这里为null
    this.resourceLoader = resourceLoader;
  	//设置主要源,也就是我们的启动主类
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
    //这里是关键,这里会判断当前SpringBoot应用程序是否为Web项目,并返回当前的项目类型
    //deduceFromClasspath是根据类路径下判断是否包含SpringBootWeb依赖,如果不包含就是NONE类型,包含就是SERVLET类型
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
    //获取并设置所有ApplicationContextInitializer实现,这些都是应用程序上下文初始化器
  	//这个接口用于在 Spring 容器执行 onRefresh 方法刷新之前执行一个回调函数
 		//通常用于向 SpringBoot 启动的容器中注入一些属性,比如ContextIdApplicationContextInitializer就是
  	//将配置中定义的 spring.application.name 属性值设定为应用程序上下文的ID
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
  	//设置应用程序监听器
    this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
  	//找到并设定当前的启动主类
    this.mainApplicationClass = this.deduceMainApplicationClass();
}

new 一个 SpringApplication 然后去走 SpringApplication的构造方法

  1. 判断是否为web应用,查看是否包含web依赖

  2. ==通过 getSpringFactoriesInstances 去获得默认的初始化器==

    • 	private <T> List<T> getSpringFactoriesInstances(Class<T> type, ArgumentResolver argumentResolver) {
      		return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, argumentResolver);
      	}
      
    • public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {
              return forResourceLocation("META-INF/spring.factories", classLoader);
          }
      
    • 其实总的来讲就是去 spring.factories 文件下寻找到默认的初始化器并装配

  3. 获取应用上下文初始化器 ApplicationContextInitializer

  4. 最后找到并设定当前的类为运行主类,也就是传进来的xxxApplication.class

3、获取配置

第二步将Spring Application的构造器走完后 接下来运行如下

public ConfigurableApplicationContext run(String... args) {
   	long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
    ConfigurableApplicationContext context = null;
    this.configureHeadlessProperty();
  	//获取所有的SpringApplicationRunListener,并通知启动事件,默认只有一个实现类EventPublishingRunListener
    //EventPublishingRunListener会将初始化各个阶段的事件转发给所有监听器
    SpringApplicationRunListeners listeners = this.getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
      	//环境配置,包括我们之前配置的多环境选择
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
      	//打印Banner,从这里开始我们就可以切切实实看到运行状了
        Banner printedBanner = this.printBanner(environment);
      	//创建ApplicationContext,也就是整个Spring应用程序的IoC容器,SSM阶段已经详细介绍过,注意这里会根据构造时得到的类型,创建不同的ApplicationContext实现类(比如Servlet环境下就是Web容器)
        context = this.createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
      	//对ApplicationContext进行前置处理,这里会将创建对象时设定的所有ApplicationContextInitializer拿来执行一次initialize方法,这也验证了我们之前的说法,这一步确实是在刷新容器之前进行的
        this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
      	//执行ApplicationContext的refresh方法,刷新容器初始化所有的Bean,这个也在SSM阶段详细介绍过了
        this.refreshContext(context);
        this.afterRefresh(context, applicationArguments);
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
        }
        listeners.started(context, timeTakenToStartup);
      	//因为所有的Bean都已经加载,这里就可以调用全部的自定义Runner实现了
        this.callRunners(context, applicationArguments);
    ...
    //结束
    return context;
}
  1. 将后续启动事件通知,启动监听器 SpringApplicationRunListener
  2. 读取配置 application.yml
  3. 打印banner,这个时候已经可以看到我们后续的一个log了
  4. 然后就去创建整个Spring最核心的 ==ApplicationContext==
  5. 初始化所有的Bean并装配进容器中
  6. 返回Context 也就是返回容器
文章作者: 面具
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 MianJu —— 这只是一个 Title 而已~
springboot java 面试 springboot java
喜欢就支持一下吧