- 3-SpringApplication的run启动方法的全生命周期函数源码
- 3.1 run方法的生命周期
- 3.2 上下文执行之前的逻辑
- 3.2.1 创建启动上下文对象
- 3.2.2 配置无头属性
- 3.2.3 运行监听器对象获取
- 3.2.4 starting回调方法的调用
前面介绍了SpringBoot应用程序SpringApplication对象的创建创建过程,接下来我们就看下它的运行方法的生命周期:
3.1 run方法的生命周期先直接贴代码,然后在代码上贴注释来看:
public ConfigurableApplicationContext run(String... args) {
//启动时间
long startTime = System.nanoTime();
//创建启动上下文对象
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
//配置无头属性
configureHeadlessProperty();
//获取运行监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
//调用启动类型的启动方法(这个需要启动类型实现对应接口)
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
//创建应用程序参数对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//准备启动环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
//配置需要忽略的Bean信息
configureIgnoreBeanInfo(environment);
//打印一个Banner提示下用户当前版本
Banner printedBanner = printBanner(environment);
//创建应用程序上下文对象
context = createApplicationContext();
//设置下上下文对象的应用程序启动器
context.setApplicationStartup(this.applicationStartup);
//准备上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
//刷新上下文
refreshContext(context);
//刷新上下文之后的逻辑
afterRefresh(context, applicationArguments);
//打印启动时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
//应用程序启动完成的start回调方法
listeners.started(context, timeTakenToStartup);
//获取所有的ApplicationRunner类型和CommandLineRunner类型对象,
//先调用ApplicationRunner类型的启动方法run
//再调用所有的CommandLineRunner的run方法
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
//失败则转换异常信息打印异常
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
//调用监听器的回调running方法
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
上面的代码看起来比较长,把他总结一下,方便理解:
-
应用程序上下文逻辑执行之前的初始化逻辑
- 启动上下文DefaultBootstrapContext对象的创建
- 配置无头属性
- 应用程序运行时SpringApplicationRunListeners对象的创建与启动方法starting的回调
- 应用程序参数对象的的初始化
- ConfigurableEnvironment环境信息的准备
- 配置需要忽略的Bean信息
- 打印一个Banner提示下用户当前版本
-
应用程序上下文执行逻辑(了解过Spring源码的同学一定知道这个上下文对象就是Spring容器启动的核心)
- 创建应用程序上下文对象
- 设置下上下文对象的应用程序启动器
- 准备上下文
- 刷新上下文(启动生命周期方法)
- 刷新上下文之后的逻
-
应用程序上下文执行之后的回调逻辑
- 打印启动时间
- 应用程序启动完成的started回调方法
- ApplicationRunner类型和CommandLineRunner类型对象的run方法回调
这个生命周期代码比较长这里我们先说一下第一部分应用程序上下文逻辑执行之前的初始化逻辑,第二部分内容比较多,我们在后面的章节中再说
3.2 上下文执行之前的逻辑 3.2.1 创建启动上下文对象这个比较简单就是创建一个对象,然后调用初始化方法
private DefaultBootstrapContext createBootstrapContext() {
//直接new一个启动上下文
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
//启动扩展的初始化方法,上一个博客我们看构造器初始化扩展的时候,当前阶段没有这个扩展
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
//返回
return bootstrapContext;
}
3.2.2 配置无头属性
configureHeadlessProperty();
Headless模式是在缺少显示屏、键盘或者鼠标是的系统配置。在java.awt.toolkit和java.awt.graphicsenvironment类中有许多方法,除了对字体、图形和打印的操作外还可以调用显示器、键盘和鼠标的方法。但是有一些类中,比如Canvas和Panel,可以在headless模式下执行。
Headless模式虽然不是我们愿意见到的,但事实上我们却常常需要在该模式下工作,尤其服务器端程序开发者。因为服务器往往可能缺少前述设备,但又需要使用他们提供的功能,生成相应的数据,以提供给客户端。
一般可以用如下方式配置,这个要注意一下如果用到了java.awt包下与图形相关的类型请不要设置为无头模式:
JAVA_OPTS="-Djava.awt.headless=true"
配置无头属性将配置中的boolean字符串转换为Boolean类型
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
3.2.3 运行监听器对象获取
SpringApplicationRunListeners listeners = getRunListeners(args);
private SpringApplicationRunListeners getRunListeners(String[] args) {
//这个是参数类型数组
Class>[] types = new Class>[] { SpringApplication.class, String[].class };
//这里分为两步,1)通过扩展机制获取 参数类型为types,参数为args 类型为SpringApplicationRunListener的扩展对象
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
SpringApplication运行方法的监听器。SpringApplicationRunListeners是通过SpringFactoriesLoader加载的,应该声明一个公共构造函数,该构造函数接受SpringApplication实例和参数字符串[]。每次运行都将创建一个新的SpringApplicationRunListener实例。
这个扩展方法和上一节看的略微不同我们还是来看下:
privateCollection getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) { //获取当的类加载器 resourceLoader的类加载器优先 ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates //借助SpringFactoriesLoader来获取所有扩展的扩展名字 Set names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //创建所有扩展的对象,这个方法是 List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); //根据Order对扩展对象进行排序 AnnotationAwareOrderComparator.sort(instances); return instances; }
目前支持的扩展类型为org.springframework.boot.context.event.EventPublishingRunListener
创建SpringApplicationRunListeners类型对象
这个类型和前面获取的扩展集合是不一样的要注意,:这个类型后面多了个s,前面那个没有s 这个是用来存储与管理调用那个集合的,
当后面调用运行监听回调方法的时候会调用这个类型的方法,这个方法的来一个一个调用扫描到的扩展集合中的对象列表
SpringApplicationRunListeners(Log log, Collection extends SpringApplicationRunListener> listeners,
ApplicationStartup applicationStartup) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
this.applicationStartup = applicationStartup;
}
3.2.4 starting回调方法的调用
SpringApplicationRunListeners的第一个回调方法的调用:
listeners.starting(bootstrapContext, this.mainApplicationClass);
void starting(ConfigurableBootstrapContext bootstrapContext, Class> mainApplicationClass) {
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
private void doWithListeners(String stepName, ConsumerlistenerAction, Consumer stepAction) { StartupStep step = this.applicationStartup.start(stepName); this.listeners.forEach(listenerAction); if (stepAction != null) { stepAction.accept(step); } step.end(); }


![[Spring-Boot 2.6.6源码解析系列]-3-SpringApplication的run启动方法的全生命周期函数源码 [Spring-Boot 2.6.6源码解析系列]-3-SpringApplication的run启动方法的全生命周期函数源码](http://www.mshxw.com/aiimages/31/839139.png)
