springboot启动源码解析(三):初始化启动上下文、初始化监听器列表、发布开始启动事件
此章节主要对springboot启动过程中,发生的【初始化启动上下文】、【初始化监听器列表】、【发布springboot开始启动事件】进行源码解析,对应的代码如图1所示:
图1:
// 首先初始化一个计时器,并开始计时
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 初始化启动上下文
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
// 设置系统参数:java.awt.headless(在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式,例如Linux服务器)
configureHeadlessProperty();
// 初始化监听器列表
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布springboot开始启动事件
listeners.starting(bootstrapContext, this.mainApplicationClass);
初始化启动上下文
图2:
private DefaultBootstrapContext createBootstrapContext() {DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();this.bootstrappers.forEach((initializer) -> initializer.intitialize(bootstrapContext));return bootstrapContext;
}
点击进入【createBootstrapContext()】方法,可以看到首先获取了一个DefaultBootstrapContext类实例 -> bootstrapContext,然后遍历this.bootstrappers对bootstrapContext进行初始化,由于this.bootstrappers中无任何元素(见springboot启动源码解析(一):SpringApplication初始化),故对bootstrapContext未执行任何操作,最后将bootstrapContext返回。
设置系统参数:java.awt.headless
图3:
private void configureHeadlessProperty() {System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
在系统可能缺少显示设备、键盘或鼠标这些外设的情况下可以使用该模式,例如Linux服务器
初始化监听器列表
图4:
private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),this.applicationStartup);
}
如图4所示,在【getRunListeners】方法中,一共进行了3步操作:
1、获取了一个class数组types
其中的元素分别为:SpringApplication和String数组的class对象,types主要用于在后序反射初始化对象时,获取到目标的构造方法。
2、获取SpringApplicationRunListener实例对象。
【getSpringFactoriesInstances】方法的目的为:获取并维护SpringApplicationRunListener接口的子类对象列表。在后续的启动过程中,交由listeners发布的事件,实际上是遍历其内部管理的SpringApplicationRunListener对象列表进行发布的。
最终通过getSpringFactoriesInstances获取到的SpringApplicationRunListener类型的对象有且仅有一个:EventPublishingRunListener实例对象。
实例化EventPublishingRunListener对象时,在它的构造方法中(图5)对成员变量进行了一些初始化操作。例如向initialMulticaster中加入了application中维护的9个监听器对象(application监听器的由来详见:springboot启动源码解析(一):SpringApplication初始化),它们分别为:EnvironmentPostProcessorApplicationListener、AnsiOutputApplicationListener、LoggingApplicationListener、BackgroundPreinitializer、DelegatingApplicationListener、ParentContextCloserApplicationListener、ClearCachesApplicationListener、FileEncodingApplicationListener、LiquibaseServiceLocatorApplicationListener
图5(EventPublishingRunListener类的构造方法):
public EventPublishingRunListener(SpringApplication application, String[] args) {this.application = application;this.args = args;this.initialMulticaster = new SimpleApplicationEventMulticaster();for (ApplicationListener<?> listener : application.getListeners()) {this.initialMulticaster.addApplicationListener(listener);}
}
3、通过获取到的SpringApplicationRunListener实例,创建一个SpringApplicationRunListeners对象并返回。
注:getSpringFactoriesInstances解析请见:springboot启动源码解析(一):SpringApplication初始化
发布springboot开始启动事件
图6:
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, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {StartupStep step = this.applicationStartup.start(stepName);this.listeners.forEach(listenerAction);if (stepAction != null) {stepAction.accept(step);}step.end();
}
初始化启动上下文和监听器后,通过调用监听器中的starting方法发布【springboot开始启动】事件,在starting方法中,实际通过调用doWithListeners方法对事件进行发布。
解析doWithListeners方法:
1、初始化一个StartupStep实例,并命名为"spring.boot.application.starting",其作用为:追踪“执行时间”或其他指标。
2、对内部维护的监听器列表进行遍历,执行传入的listenerAction。由于listeners中只包含一个EventPublishingRunListener对象,故此处是用EventPublishingRunListener实例对象执行starting(bootstrapContext)方法。
图7(EventPublishingRunListener的starting方法):
public void starting(ConfigurableBootstrapContext bootstrapContext) {this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
在EventPublishingRunListener的starting方法中,实际上是调用了其成员变量initialMulticaster的multicastEvent(ApplicationEvent event)方法。
图8:
public void multicastEvent(ApplicationEvent event) {multicastEvent(event, resolveDefaultEventType(event));
}/*** 解析事件类型*/
private ResolvableType resolveDefaultEventType(ApplicationEvent event) {return ResolvableType.forInstance(event);
}/*** 批量消费事件*/
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));Executor executor = getTaskExecutor();// 遍历符合事件的监听器,向这些监听器发布事件for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {if (executor != null) {executor.execute(() -> invokeListener(listener, event));}else {invokeListener(listener, event);}}
}
接下来进入initialMulticaster.multicastEvent(ApplicationEvent event)进行查看。
如图8所示,在 multicastEvent(final ApplicationEvent event, ResolvableType eventType)方法中:
1、调用 getApplicationListeners(event, type)能够在initialMulticaster维护的监听器列表中找到匹配该事件的监听器列表
此处符合ApplicationStartingEvent事件的监听器有:LoggingApplicationListener、BackgroundPreinitializer、DelegatingApplicationListener、LiquibaseServiceLocatorApplicationListener
2、对符合要求的监听器进行遍历,执行invokeListener(listener, event)方法
图9:(向监听器发布事件)
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {ErrorHandler errorHandler = getErrorHandler();if (errorHandler != null) {try {doInvokeListener(listener, event);}catch (Throwable err) {errorHandler.handleError(err);}}else {doInvokeListener(listener, event);}
}private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);}catch (ClassCastException ex) {String msg = ex.getMessage();if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||(event instanceof PayloadApplicationEvent &&matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {Log loggerToUse = this.lazyLogger;if (loggerToUse == null) {loggerToUse = LogFactory.getLog(getClass());this.lazyLogger = loggerToUse;}if (loggerToUse.isTraceEnabled()) {loggerToUse.trace("Non-matching event type for listener: " + listener, ex);}}else {throw ex;}}
}
由于在initialMulticaster中,线程池为null,故会直接执行invokeListener(listener, event)方法。如图9所示,invokeListener(listener, event)方法实际是调用了listener.onApplicationEvent(event)方法。
当事件为ApplicationStartingEvent时,以上4个监听器的onApplicationEvent(event)方法如图10所示,它们分别进行了以下操作:
① LoggingApplicationListener初始化了loggingSystem
② BackgroundPreinitializer:未执行任何操作
③ DelegatingApplicationListener:未执行任何操作
④ LiquibaseServiceLocatorApplicationListener:未执行任何操作
图10:
/*** 当listener为LoggingApplicationListener时*/
public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationStartingEvent) {onApplicationStartingEvent((ApplicationStartingEvent) event);}else if (event instanceof ApplicationEnvironmentPreparedEvent) {onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);}else if (event instanceof ApplicationPreparedEvent) {onApplicationPreparedEvent((ApplicationPreparedEvent) event);}else if (event instanceof ContextClosedEvent&& ((ContextClosedEvent) event).getApplicationContext().getParent() == null) {onContextClosedEvent();}else if (event instanceof ApplicationFailedEvent) {onApplicationFailedEvent();}
}private void onApplicationStartingEvent(ApplicationStartingEvent event) {// 初始化loggingSystemthis.loggingSystem = LoggingSystem.get(event.getSpringApplication().getClassLoader());this.loggingSystem.beforeInitialize();
}// ---------------------------------------------------------------/*** 当listener为BackgroundPreinitializer时,由于未识别到ApplicationStartingEvent事件,故不执行任何操作*/
public void onApplicationEvent(SpringApplicationEvent event) {if (!ENABLED) {return;}if (event instanceof ApplicationEnvironmentPreparedEvent&& preinitializationStarted.compareAndSet(false, true)) {performPreinitialization();}if ((event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent)&& preinitializationStarted.get()) {try {preinitializationComplete.await();}catch (InterruptedException ex) {Thread.currentThread().interrupt();}}
}// ---------------------------------------------------------------/*** 当listener为DelegatingApplicationListener时,由于未识别到ApplicationStartingEvent事件,故不执行任何操作*/
public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationEnvironmentPreparedEvent) {List<ApplicationListener<ApplicationEvent>> delegates = getListeners(((ApplicationEnvironmentPreparedEvent) event).getEnvironment());if (delegates.isEmpty()) {return;}this.multicaster = new SimpleApplicationEventMulticaster();for (ApplicationListener<ApplicationEvent> listener : delegates) {this.multicaster.addApplicationListener(listener);}}if (this.multicaster != null) {this.multicaster.multicastEvent(event);}
}// ---------------------------------------------------------------/*** 当listener为LiquibaseServiceLocatorApplicationListener时,由于LIQUIBASE_PRESENT为false,故不执行任何操作*/
public void onApplicationEvent(ApplicationStartingEvent event) {if (LIQUIBASE_PRESENT) {new LiquibasePresent().replaceServiceLocator();}
}
springboot启动源码解析(三):初始化启动上下文、初始化监听器列表、发布开始启动事件相关推荐
- Myth源码解析系列之五- 服务启动源码解析
通过前面几篇文章,我们搭建了环境,也进行了分布式事务服务的体验,相信大家对myth也有了一个大体直观的了解,接下来我们将正式步入源码解析之旅~~ order服务启动源码解析(myth-demo-spr ...
- Disruptor源码解析三 RingBuffer解析
目录 系列索引 前言 主要内容 RingBuffer的要点 源码解析 系列索引 Disruptor源码解析一 Disruptor高性能之道 Disruptor源码解析二 Sequence相关类解析 D ...
- SpringBoot入门-源码解析(雷神)
一.Spring Boot入门 视频学习资料(雷神): https://www.bilibili.com/video/BV19K4y1L7MT?p=1 github: https://github.c ...
- Spring源码解析 -- SpringWeb请求映射Map初始化
简介 在上篇文章中,大致解析了Spring如何将请求路径与处理方法进行映射,但映射相关的初始化对于我们来说还是一团迷雾 本篇文章就来探索下,请求路径和处理方法的映射,是如何进行初始化的 概览 基于上篇 ...
- ReactiveSwift源码解析(三) Signal代码的基本实现
上篇博客我们详细的聊了ReactiveSwift源码中的Bag容器,详情请参见<ReactiveSwift源码解析之Bag容器>.本篇博客我们就来聊一下信号量,也就是Signal的的几种状 ...
- 并发编程与源码解析 (三)
并发编程 (三) 1 Fork/Join分解合并框架 1.1 什么是fork/join Fork/Join框架是JDK1.7提供的一个用于并行执行任务的框架,开发者可以在不去了解如Thread.R ...
- OkHttp3源码解析(三)——连接池复用
OKHttp3源码解析系列 OkHttp3源码解析(一)之请求流程 OkHttp3源码解析(二)--拦截器链和缓存策略 本文基于OkHttp3的3.11.0版本 implementation 'com ...
- 前端入门之(vuex源码解析三)
上两节前端入门之(vuex源码解析二)我们把vuex的源码大概的撸了一遍,还剩下(插件.getters跟module),我们继续哈~ 插件童鞋们可以去看看vuex在各个浏览器的状态显示插件,小伙伴可以 ...
- 拆轮子-RxDownload2源码解析(三)
本文为博主原创文章,未经允许不得转载 造轮子者:Season_zlc 轮子用法请戳作者链接 ↑ 前言 本文主要讲述 RxDownload2 的多线程断点下载技术. 断点下载技术前提 服务器必须支持按 ...
最新文章
- 3.5 实例讲解Lucene索引的结构设计
- Ember.js学习项目源码发布
- 物理搬砖问题_人物专访|从“搬砖烧炉工”到MIT物理PhD, 很普通、很坚持、很热爱...
- 2018上半年区块链安全报告
- 通过 a 标签下载文件
- 关于吾爱(52)破解网注册时临时安全验证码以及填完信息没有确认或提交按钮的问题
- java工程师优秀简历模板,这原因我服了
- html a标签填写header,header中添加a标签,点击无反应
- 硬盘格式化数据恢复的软件推荐
- 跳过H5页面视频的方法
- 【算法导论】 二叉搜索树、AVL树、和红黑树
- C++一本通题库1010
- linux升级之后黑屏,fedora升级到28之后gnome登录黑屏的解决方法
- 关于修改虚拟机内存,和开启 硬件加速
- 【SQL server】关系运算
- 英文星期的来历(都是来自神人)
- 微信小程序分析送积分功能如何实现_微信小程序积分商城系统如何裂变营销购物?...
- 天体运行轨迹_太阳系内天体的运行轨迹是什么?开普勒为什么这么伟大?
- 东南大学自然辩证法概论期末总结
- Python 入门指南第三节 | Python 简介