SpringBoot启动运行项目是运行流程如上图所示
1. ApplicationStartingEvent`在运行开始时但在任何处理之前发送,除了侦听器和初始化程序的注册。
2. `ApplicationEnvironmentPreparedEvent`当`Environment`在上下文中使用的 已知但在创建上下文之前发送一个。
3. `ApplicationContextInitializedEvent`当`ApplicationContext`准备好并调用 Application Context Initializers 但在加载任何 bean 定义之前发送一个。
4. `ApplicationPreparedEvent`在刷新开始之前但在加载 bean 定义之后发送一个。
5. An`ApplicationStartedEvent`在上下文刷新之后但在调用任何应用程序和命令行运行程序之前发送。
6. `AvailabilityChangeEvent`在 with 之后立即发送一个`LivenessState.CORRECT`,表明应用程序被认为是活动的。
7. `ApplicationReadyEvent`在调用任何[应用程序和命令行运行程序](https://docs.spring.io/spring-boot/docs/2.4.13/reference/html/spring-boot-features.html#boot-features-command-line-runner)后发送一个。
8. `AvailabilityChangeEvent`在 with 之后立即发送一个`ReadinessState.ACCEPTING_TRAFFIC`,表示应用程序已准备好为请求提供服务。
9. 如果`ApplicationFailedEvent`启动时出现异常,则发送一个。
结论一:
- 1 判断项目类型
- 2 推断当前主类
- 3 设置监听器 获取上下文 全面接管SpringMvc配置
- 4 找到运行主类\
结论二:
.初始化SpringApplication 从spring.factories 读取listener ApplicationContextlnitializer
运行run方法
.读取 环境变量 配置信息....
.创建springApplication上下文:ServletWebServerApplicationContext
预初始化上下文:读取启动类
.调用refresh 加载Ioc容器 加载所有的自动配置类 创建servlet容器
在这个过程中springboot会调用很多监听器对外进行扩展
package com.spring.springboot0907web; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; //深入源码 @SpringBootApplication public class Springboot0907WebApplication { public static void main(String[] args) { //调用 SpringApplication.run 来启用 SpringApplication.run(Springboot0907WebApplication.class, args); } }
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); } public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return (new SpringApplication(primarySources)).run(args); }
上面的代码不做介绍重点是今天要讲的Run方法:在这个方法中他要运行很多信息:
用户进入到Run方法中如下面的图所示
public ConfigurableApplicationContext run(String... args) { long startTime = System.nanoTime(); DefaultBootstrapContext bootstrapContext = this.createBootstrapContext(); ConfigurableApplicationContext context = null; this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(bootstrapContext, this.mainApplicationClass); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment); context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup); this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); 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); this.callRunners(context, applicationArguments); } catch (Throwable var12) { this.handleRunFailure(context, var12, listeners); throw new IllegalStateException(var12); }
对上面的源码进行解析:
1 用来记录当前springBoot启动耗时 和记录启动时间
long startTime = System.nanoTime(); DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
2 他是任何spring上下文的接口 所以他可以接收任何的ApplicationContext
ConfigurableApplicationContext context = null;
3 开启了Headless模式
this.configureHeadlessProperty()
4 去SpringFactories中读取 SpringApplicationRunListeners 的组件 又是用来发布监听器的
SpringApplicationRunListeners listeners = this.getRunListeners(args); //点击getRunListeners(args)
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class[]{SpringApplication.class, String[].class}; return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup); }
5 发布 ApplicationStartingEvent事件 在运行时发送
listeners.starting(bootstrapContext, this.mainApplicationClass);
6 根据命令行参数 实例一个 applicationArguments 对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
7 预初始化环境:读取变量 读取配置文件信息(基于监听器)
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments); this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment);
插入一点:prepareEnvironment 介绍
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { ConfigurableEnvironment environment = this.getOrCreateEnvironment(); this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs()); 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) { EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader()); environment = environmentConverter.convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, this.deduceEnvironmentClass()); } ConfigurationPropertySources.attach((Environment)environment); return (ConfigurableEnvironment)environment; }
1 根据webApplicationType 创建Environment 创建 就会读取: java环境 变量和系统环境变量
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
2 将命令行参数读取环境变量中
this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
3 将PropertieSource的配置信息放在第一 位,因 为读取配置文件@PropertieSource优先级是最低的
ConfigurationPropertySources.attach((Environment)environment);
4 发布了ApplicationEnvironmentPreparedEvent 的监听器 读取了全局配置文件
listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);
5 将所有spring .main开头的配置信息绑定SpringApplication
this.bindToSpringApplication((ConfigurableEnvironment)environment);
8 忽略beaninfo 的bean
this.configureIgnoreBeanInfo(environment);
9 打印Bean横幅
Banner printedBanner = this.printBanner(environment);
10 根据 webApplicationContext创建Spring上下文
context = this.createApplicationContext(); context.setApplicationStartup(this.applicationStartup);
11 准备应用上下文
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
12. 刷新应用上下文
this.refreshContext(context);
13 刷新完成后调
this.afterRefresh(context, applicationArguments);
14 计算启动时间,JDK Duration类表示秒或纳秒时间间隔,
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
//15. 打印启动日志 if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup); } // 16. 开始执行监听器 listeners.started(context, timeTakenToStartup); // 17. 从应用上下文中获取ApplicationRunner和CommandLinerRunner类型的Bean并调用他们的run方法 this.callRunners(context, applicationArguments); } catch (Throwable var12) { // 处理异常 this.handleRunFailure(context, var12, listeners); throw new IllegalStateException(var12); } try { Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime); // 18. 准备监听器 listeners.ready(context, timeTakenToReady); // 19返回上下文 return context; } catch (Throwable var11) { this.handleRunFailure(context, var11, (SpringApplicationRunListeners)null); throw new IllegalStateException(var11); }
今天的Run方法的启动流程到这里结束了.结论自己总结
图一
图二