SpringBoot启动过程分析
注意:本文分析的是SpringBoot的启动流程,关于自动装配,如何加载application.yml配置等内容在之后的文章分析。SpringBoot是基于Spring开发的,所以分析SpringBoot启动过程,就是在分析SpringBoot如何创建一个Spring容器的,以及在创建过程中有哪些可供开发者个性化开发使用的钩子。(文末有总结)
SpringApplication.run()方法究竟是如何启动SpringBoot项目的?
可以看到,主要分为两部分,一部分是new SpringApplication,另一部分是run。
实例化SpringSpplication
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推测启动的类型,是Web启动还是非Web等
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 在spring容器refresh之前会调用initialzers的方法,可以用它在容器refresh来修改
// 容器context的各种配置,如environment等
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 监听器,用于监听springboot生命周期的,getSpringFactoriesInstances方法是通过spring.factories文件
//找到ApplicationListener.class作为KEY对应的Value,并使用反射实例化他们的,这里是实例化好的了,
// 上面的Initializer同理
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 运行SpringApplication.run的类
this.mainApplicationClass = deduceMainApplicationClass();
}
Run方法
public ConfigurableApplicationContext run(String... args) {
// 用于记录spring的启动时间的计时器一样的东西
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 利用getSpringFactoriesInstances方法获取到SpringApplicationRunListener
// 的实现类EventPublishingRunListener,这个类相当于一个被观察者,而在前面构造方法
// setListeners的设置的ApplicationListener的实现类相当于观察者,观察者都注册到了
// 被观察者(实例化被观察者时注册的),后续spirngboot生命周期的事件就通过这个被观察者 发布给了观察者。
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布springboot启动中的事件
listeners.starting();
try {
// 启动参数,即run方法传入的参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 获取到环境,即各种配置的属性,application.yml,环境变量,系统参数那些构造的容器环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
// 准备Banner打印器 - 就是启动Spring Boot的时候打印在console上的ASCII艺术字体
Banner printedBanner = printBanner(environment);
// 创建Spring容器
context = createApplicationContext();
// 异常报告器,可以处理springboot的生命周期的异常
// 也是通过getSpringFactoriesInstances 方法来获取配置的异常类名称并实例化所有的异常处理类
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备spring容器的配置,如容器的environment,
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新容器,spring的东西,刷新出一个完整的容器
refreshContext(context);
// 刷新完了
afterRefresh(context, applicationArguments);
// springboot启动完成
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 通知listener started事件
listeners.started(context);
// 调用自定义的XXXRunner类,使得在项目启动完成后立即执行一些特定的程序
// SpringBoot提供了ApplicationRunner和CommandLineRunner两种
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// 通知listener 容器running事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
总结
在启动过程中,可供开发者个性化开发的钩子有Initiailzers和listenrs,其中Initializer是在refresh spring容器前会调用的,可以用它修改容器配置;isteners监听springboot的整个启动周期,用处很大,基于它可以个性化开发springboot;Runner是在整个springboot流程之后会被调用的,可以用它做一些SpringBoot启动完想要做的事情。