SpringApplication对象的创建
用过SpringBoot的都知道,在项目启动入口的主类main()方法就开启了SpringBoot的启动之路  在这里打上一个断点启动  可以发现它底层是先创建一个SpringApplication对象,然后调用run方法,点进其构造器可以发现
this.primarySources
 除去上面的一些基本的赋值,方法来到这,可以见到它为主资源this.primarySources赋值,就是我们的主类
this.webApplicationType
点击进入deduceFromClasspath()方法  他会判断我们应用的类型进行返回赋值  本环境采用的是最纯净的环境,类型为None,在WEB开发中返回的类型一般是SERVLET
setInitializers
  它是给自己的一个Initializers属性赋值,会去META-INF 目录下找spring.factories里的ApplicationContextInitializer,最终赋值给自己的属性     
发现Spring-boot-2.6.3.jar下有5个,spring-boot-autoconfigure-2.6.3.jar下有两个,一共七个
setListeners
会去META-INF 目录下找spring.factories里的ApplicationListener,最终赋值给自己的属性listeners,和上个类似 
调的是同一个方法,根据入参找类型  
发现Spring-boot-2.6.3.jar下有7个,spring-boot-autoconfigure-2.6.3.jar下有1个,一共8个
this.mainApplicationClass
  其大概逻辑就是找到第一个带有main()方法的程序,就是我们的主程序
小结
创建SpringApplication主要干了这些事情
- primarySources 保存当前主启动类信息
- webApplicationType 判断当前应用类型,web应用中一般为Servlet应用
- setInitializers 去spring.factories找ApplicationContextInitializer 然后赋值给属性Initializers
- setListeners 去spring.factories找ApplicationListener 然后赋值给属性listeners
SpringApplication运行过程
当SpringApplication对象创建完成后,就是调用其Run方法,run方法代码如下
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
Banner printedBanner = this.printBanner(environment);
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
this.refreshContext(context);
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);
}
}
StopWatch.start()
进入到start方法
public void start() throws IllegalStateException {
this.start("");
}
可见其作用是纪录程序启动的当前时间
public void start(String taskName) throws IllegalStateException {
if (this.currentTaskName != null) {
throw new IllegalStateException("Can't start StopWatch: it's already running");
} else {
this.currentTaskName = taskName;
this.startTimeNanos = System.nanoTime();
}
}
configureHeadlessProperty()
 让当前应用进入headless模式
getRunListeners(args)
 相信大家对这个方法已经很熟悉了,去META-INF/spring.factories下找SpringApplicationRunListener,获取所有运行监听器RunListener
listeners.starting()
监听器通知项目正在启动中  不难看出,其是遍历通知
applicationArguments
保存命令行参数args 
prepareEnvironment

getOrCreateEnvironment()
 可以看见其为我们装载一个SERVLET环境
this.configureEnvironment
 其为我们配置类型转换器  点进configurePropertySources方法  environment.getPropertySources();是得到所有PropertySources  返回上级配置激活Profile信息this.configureProfiles(environment, args);
listeners.environmentPrepared
 清晰得知,遍历每一个监听器,通知我们的应用环境已经准备好了 
创建IOC容器createApplicationContext
  根据web类型创建一个IOC容器
准备IOC容器上下文prepareContext

context.setEnvironment(environment)

postProcessApplicationContext()

applyInitializers
 getInitializers()就是我们在创建SpringApplication对象是存进去的ApplicationContextInitializer
listeners.contextPrepared()

 也是遍历调用RunListener的contextPrepareds
getBeanFactory()
beanFactory.registerSingleton

arg参数在spring中也是个组件,组件名叫springApplicationArguments,注册到bean工厂中
listeners.contextLoaded();
 老规矩 监听器调用contextload方法
refreshContext(核心源码)
作用是刷新IOC容器,创建容器中的所有组件
  
step into ((AbstractApplicationContext)applicationContext).refresh()  如图所示,springboot组件以及其自动装配的流程都在这一步发生
afterRefresh
this.afterRefresh(context, applicationArguments); 容器刷新完成后工作
stopWatch.stop();
记录运行完成后的结束时间
listeners.started(context);
老朋友了,遍历listeners调用started方法
callRunners

- 获取容器中的ApplicationRunner
- 获取容器中的CommandLineRunner
- 合并所有runner并按照@Order注解进行排序(越小 优先级越高)
- 遍历所有的runner,调用run方法,先A后C
handleRunFailure
如果有异常,遍历调用listener的Failed方法 
listeners.running(context)
调用所有监听器的run方法,如果有异常则调用failed方法
return context
返回IOC容器
总结
- StopWatch记录应用的启动时间
- 让当前应用进入到headless模式。java.awt.headless
- 获取所有的RunListener(运行监视器)【为了方便所有监视器进行事件感知】getSpringFactoriesInstances 去spring.factories找 SpringApplicationRunListener.
- 遍历 SpringApplicationRunListener 调用 starting 方法;相当于通知所有感兴趣系统正在启动过程的人,项目正在 starting。
- 保存命令行参数;ApplicationArguments。
- 准备环境 prepareEnvironment()
- 返回或者创建基础环境信息对象。StandardServletEnvironment
- 读取所有的配置源的配置属性值。得到所有的PropertySources,@PropertySources即加载外部所有的配置文件xxx.properties
- 监听器调用 listener.environmentPrepared();通知所有的监听器当前环境准备完成
- 创建IOC容器(createApplicationContext())
- 根据项目类型(Servlet)创建容器
- 当前会创建 AnnotationConfigServletWebServerApplicationContext
- 准备ApplicationContext IOC容器的基本信息 prepareContext()
- 保存环境信息
- IOC容器的后置处理流程
- 应用初始化器;applyInitializers;
- 遍历所有的 ApplicationContextInitializer 。调用 initialize.。来对ioc容器进行初始化扩展功能
- 遍历所有的 listener 调用 contextPrepared。EventPublishRunListenr;通知所有的监听器contextPrepared
- 所有的监听器 调用 contextLoaded。通知所有的监听器 contextLoaded
- 刷新IOC容器 refreshContext【核心源码】
- 容器刷新完成后工作?afterRefresh
- 所有监听器 调用 listeners.started(context); 通知所有的监听器 started
- 调用所有runners;callRunners()
- 获取容器中的ApplicationRunner
- 获取容器中的 CommandLineRunner
- 合并所有runner并且按照@Order进行排序
- 遍历所有的runner。先ApplicationRunner 后CommandLineRunner调用 run 方法
- 如果以上有异常,调用Listener的failed
- 调用所有监听器的 running 方法 listeners.running(context); 通知所有的监听器 running
- running如果有问题。继续通知 failed 。调用所有 Listener 的 failed;通知所有的监听器 failed
|