SpringBoot启动原理

Posted asleaf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot启动原理相关的知识,希望对你有一定的参考价值。

从一个SpringBoot启动类说起。
@SpringBootApplication
public class DemoApplication{
    public static void main(Stirng[] args){
        SpringApplication.run(DemoApplication.class, args);
    }
}
@SpringBootApplication是以下三个Annotation的复合:
  • @Configuration:SpringBoot启动类本身也是一个配置类。

  • @EnableAutoConfiguration:作为一个复合Annotation,其定义中最关键的是@Import(AutoConfigurationImportSelector.class),在AutoConfigurationImportSelector类中,通过调用SpringFactoriesLoader的方法从classpath中搜寻所有META-INF/spring.factories配置文件,将其中自动配置项通过反射实例化为对应IoC配置类,然后汇总为一个加载到IoC容器中。

  • @ComponentScan:自动扫描并加载符合条件的组件。

 SpringBoot应用的启动流程:

 1. SpringApplication.run()的运行会创建一个SpringApplication实例,在其初始化时会收集各种条件和回调接口,例如,判断是否为web类型、加载ApplicationContextInitializer和  ApplicationListener(这里用到了SpringFactoriesLoader类的方法)、推断并设置main方法的定义类。以下所有的this指针均指向SpringApplication实例。

public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {   
    //...
    this.webApplicationType = WebApplicationType.deduceFromClasspath();                         
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }

 2. 进入run方法的逻辑中,通过SpringFactoriesLoader获得 SpringApplicationRunListeners的实例 listeners (其中封装了一个SpringApplicationRunListener集合),执行listeners.starting(),表示“SpringBoot应用开始执行!”。

SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();

 3. 创建Environment,配置其PropertySorce以及Profile。

ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);

 在prepareEnvironment方法中执行了listeners的environmentPrepared方法,表示“SpringBoot应用的Environment准备好了!”。

listeners.environmentPrepared((ConfigurableEnvironment)environment);

 4. 创建ApplicationContext,这里会根据用户是否明确设置了applicationContextClass类型以及初始阶段的推断结果,决定当前的ApplicationContext类型。

context = this.createApplicationContext();

 配置ApplicationContext。

this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);

 在prepareContext方法中设置了Environment,又调用了ApplicationContextInitializer的initialize方法对已经创建好的context做进一步的处理,然后执行listeners的contextPrepared方法,表示“SpringBoot应用的ApplicationContext准备好了!”。

private void prepareContext(//...) {
    context.setEnvironment(environment);
    this.postProcessApplicationContext(context);
    this.applyInitializers(context);
    listeners.contextPrepared(context);
    //...
}

 5. 最核心的一步:同样是在prepareContext方法中,将之前@EnableAutoConfiguration获取的所有配置以及其他形式的IoC容器配置加载到已经准备好的context中,然后执行listeners 的contextLoaded方法,表示“SpringBoot应用的ApplicationContext装填完毕!”。

private void prepareContext(//...) {
    //...
    Set<Object> sources = this.getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    this.load(context, sources.toArray(new Object[0]));
    listeners.contextLoaded(context);
}

 6. 执行refreshContext方法,最终调用的是ApplicationContext的refresh方法,完成IoC容器可用的最后一道工序。另外,这里还判断了一下是否需要添加ShutdownHook。

private void refreshContext(ConfigurableApplicationContext context) {
    this.refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        } catch (AccessControlException var3) {
        }
    }
}

 7. 执行afterRefresh方法,其实什么也没有做。

protected void afterRefresh(//...) {
}

 8. 执行listeners的started方法,表示“SpringBoot应用已经启动!”。

listeners.started(context);

 9. 查找ApplicationContext中是否注册有ApplicationRunner和CommandLineRunner,如果有,则执行它们。

this.callRunners(context, applicationArguments);
private void callRunners(ApplicationContext context, ApplicationArguments args) {
    //...
    runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
    runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
    //...
}

 10. 正常情况下,到这里就已经完成了 (如果整个过程出现异常,那么会有详细的异常处理类负责检测)。最后还有一个listeners的running方法可以进一步检测容器是否正常。

try {
    listeners.running(context);
    return context;
} catch (Throwable var9) {
    //...
    throw new IllegalStateException(var9);
}

 整个过程看起来冗长无比,但忽略其中的事件通知等逻辑,那么这个启动过程可以精简为:

  1. 初始化SpringApplication,收集各种条件和回调接口。

  2. 创建并配置Environment。

  3. 创建并初始化ApplicationContext,设置Environment,加载自动配置等。

  4. 更新ApplicationContext,完成最终程序启动。

 

以上是关于SpringBoot启动原理的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot Starter运行原理代码解析

SPRINGBOOT启动原理(基于2.x版本)-SpringFactoriesLoader

SpringBoot启动原理解析

SpringBoot启动原理

SpringBoot启动原理解析

springboot启动流程构造SpringApplication实例对象