本文涉及到Spring的监听器,如果不太了解请先阅读之前的Spring监听器的文章。

SpringBoot事件监听器初始化

SpringBoot中默认定义了11个事件监听器对象,全部定义在META-INF/spring.factories文件中。分别是:

org.springframework.boot.ClearCachesApplicationListener
org.springframework.boot.builder.ParentContextCloserApplicationListener
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor
org.springframework.boot.context.FileEncodingApplicationListener
org.springframework.boot.context.config.AnsiOutputApplicationListener
org.springframework.boot.context.config.ConfigFileApplicationListener
org.springframework.boot.context.config.DelegatingApplicationListener
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener
org.springframework.boot.context.logging.LoggingApplicationListener
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener
org.springframework.boot.autoconfigure.BackgroundPreinitializer

在SpringApplication的构造方法中会对这些默认的事件监听器进行实例化并且给this.listeners属性赋值。代码如下:

// 从spring.factories 文件中获取 ApplicationListener 监听器的实现类
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

SpringApplicationRunListeners 对象初始化,在SpringApplication.run()方法中会根据spring.factories文件中配置的SpringApplicationRunListener接口和已经创建好的SpringApplication对象来创建SpringApplicationRunListener 对象,最终会赋给SpringApplicationRunListeners 对象的this.listeners属性。代码如下:

// 生成事件发布对象
// 创建 SpringApplicationRunListeners 并把 默认的事件监听器赋值给 该对象
// 从 spring.factories文件中加载 SpringApplicationRunListener 的接口实现类
// 具体加载的是 EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);

SpringApplicationRunListener 在spring.factories文件中配置为EventPublishingRunListener对象。代码如下:

org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

EventPublishingRunListener 中会创建具体的多播器对象SimpleApplicationEventMulticaster,会把SpringApplication对象赋值给this.application属性,会把SpringApplication对象中的事件监听器集合遍历赋值给多播器里面的事件监听器集合。代码如下:

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);
}
}

至此,SpringBoot的事件监听发布器初始化完成。

SpringBoot事件监听器调用过程

整个SpringBoot的事件监听器调用过程散布在整个SpringBoot生命周期过程中。总共有8个发布事件的方法,在8个不同的地方调用。

starting事件:在SpringBoot开始创建context之前会调用SpringApplicationRunListeners的starting()方法,发布starting事件,在EventPublishingRunListener中代码如下:

@Override
public void starting() {
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}

在多播器中找到所有符合事件类型的事件监听器对象进行缓存。代码如下,

@Override
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);
}
}
}

getApplicationListeners中会根据事件监听器对象和事件类型在多播器的监听器对象中查找满足条件的事件监听器对象,并进行缓存

invokeListener 先判断是否有设置错误处理程序,如果有则需要用错误处理程序来处理事件监听器中发生的异常。

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
// 获取此多播器的当前错误处理程序
ErrorHandler errorHandler = getErrorHandler();
// 如果errorHandler不为null
if (errorHandler != null) {
try {
// 回调listener的onApplicationEvent方法,传入event
doInvokeListener(listener, event);
}
catch (Throwable err) {
// 交给errorHandler接收处理err
errorHandler.handleError(err);
}
}
else {
// 回调listener的onApplicationEvent方法,传入event
doInvokeListener(listener, event);
}
}

doInvokeListener具体调用listener的onApplicationEvent方法,传入event。

/**
* 回调listener的onApplicationEvent方法,传入 event
* @param listener
* @param event
*/
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
//回调listener的onApplicationEvent方法,传入 event:contextrefreshListener:onapplicaitonEvent:FrameworkServlet.this.onApplicationEvent()
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
//获取异常信息
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception and just log a debug message.
Log logger = LogFactory.getLog(getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
//抛出异常
throw ex;
}
}
}

environmentPrepared事件:环境变量准备事件,在创建好环境变量之后进行发布。在EventPublishingRunListener中代码如下,

public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}

contextPrepared事件:应用程序上下文准备事件,在创建好ApplicationContext之后立马调用。在EventPublishingRunListener中代码如下

public void contextPrepared(ConfigurableApplicationContext context) {
this.initialMulticaster
.multicastEvent(new ApplicationContextInitializedEvent(this.application, this.args, context));
}

contextLoaded事件:应用程序上下文加载事件,在主配置类注册到ApplicationContext中之后会进行调用。在进行发布ApplicationPreparedEvent事件之前,先把EventPublishingRunListener 的监听器对象都注入到ApplicationContext中。在EventPublishingRunListener中代码如下

public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}

started事件:应用程序启动完成事件,在应用程序完成refresh之后进行调用。在EventPublishingRunListener中代码如下

public void started(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
}

running事件:应用程序上下文ApplicationContext对象创建好之后进行发布该事件。在EventPublishingRunListener中代码如下

@Override
public void running(ConfigurableApplicationContext context) {
context.publishEvent(new ApplicationReadyEvent(this.application, this.args, context));
}

failed事件:在整个SpringBoot对象的生命周期过程,如果出现异常则发布该事件。在EventPublishingRunListener中代码如下

public void failed(ConfigurableApplicationContext context, Throwable exception) {
ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
if (context != null && context.isActive()) {
// Listeners have been registered to the application context so we should
// use it at this point if we can
context.publishEvent(event);
}
else {
// An inactive context may not have a multicaster so we use our multicaster to
// call all of the context's listeners instead
if (context instanceof AbstractApplicationContext) {
for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
.getApplicationListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
this.initialMulticaster.multicastEvent(event);
}
}

SpringBoot事件监听器和Spring的集成

一般来说我们创建事件监听器只会只用Spring的创建方式,不会用SpringBoot的方式。但是通过代码发现,通过SpringBoot方式创建的事件监听器会自动注入到Spring容器的多播器中。这是怎么做到?一起来分析下。

两种方式的区别

SpringBoot方式:事件监听器只能通过实现ApplicationListener接口并且通过在META-INF/spring.factories文件添加配置进行创建。创建之后会存在SpringApplicationRunListener对象的事件多播器中,并且多播器是SimpleApplicationEventMulticaster类型,不能进行重写,是同步方式的多播器。

Spring方式:事件监听器可以通过实现ApplicationListener接口或者@EventListener注解来实现,然后使用Spring的注入@Component注解即可创建。Spring的多播器默认是SimpleApplicationEventMulticaster类型,可以对Spring容器的多播器进行重写。

集成原理

SpringBoot先创建自己的事件发布器对象SpringApplicationRunListeners ,并且在整个生命周期中发布不同事件,然后再把其中的事件监听器添加到应用程序上下文ApplicationContext(Spring容器)中。Spring容器在之后初始化的过程中会把这些事件监听器加载到Spring的多播器中,这样就完成了集成。在SpringBoot启动的生命周期过程中,有两个地方可以对应用程序上下文ApplicationContext进行监听器的创建。

  1. 在Spring Application的run()中的prepareContext()方法中,会执行applyInitializers方法。
// 初始化 context,在具体的 ApplicationContextInitializer 类中给 context 添加相关的事件监听器
applyInitializers(context);

在该方法中会遍历执行SpringApplication中创建的ApplicationContextInitializer接口对象。其中RSocketPortInfoApplicationContextInitializer对象和RSocketPortInfoApplicationContextInitializer对象以及ConditionEvaluationReportLoggingListener对象都会对ApplicationContext添加事件监听器。

  1. 在Spring Application的run()中的prepareContext()方法中,会执行listeners.contextLoaded()方法。
// 发布 contextLoaded 事件 ,把 SpringApplication 中的事件监听器 添加给 context 的监听器列表
listeners.contextLoaded(context);

该方法中会先把EventPublishingRunListener 中的监听器对象都注入到ApplicationContext中,再进行发布ApplicationPreparedEvent事件。具体代码在EventPublishingRunListener代码如下,

public void contextLoaded(ConfigurableApplicationContext context) {
for (ApplicationListener<?> listener : this.application.getListeners()) {
if (listener instanceof ApplicationContextAware) {
((ApplicationContextAware) listener).setApplicationContext(context);
}
context.addApplicationListener(listener);
}
this.initialMulticaster.multicastEvent(new ApplicationPreparedEvent(this.application, this.args, context));
}

SpringBoot事件监听器源码分析的更多相关文章

  1. ApplicationEvent事件机制源码分析

    <spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...

  2. Zepto事件模块源码分析

    Zepto事件模块源码分析 一.保存事件数据的handlers 我们知道js原生api中要移除事件,需要传入绑定时的回调函数.而Zepto则可以不传入回调函数,直接移除对应类型的所有事件.原因就在于Z ...

  3. springboot整合mybatis源码分析

    springboot整合mybatis源码分析 本文主要讲述mybatis在springboot中是如何被加载执行的,由于涉及的内容会比较多,所以这次只会对调用关系及关键代码点进行讲解,为了避免文章太 ...

  4. tomcat源码--springboot整合tomcat源码分析

    1.测试代码,一个简单的springboot web项目:地址:https://gitee.com/yangxioahui/demo_mybatis.git 一:tomcat的主要架构:1.如果我们下 ...

  5. 原创001 | 搭上SpringBoot自动注入源码分析专车

    前言 如果这是你第二次看到师长的文章,说明你在觊觎我的美色!O(∩_∩)O哈哈~ 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 本系列为SpringBoot深度源码专车系列,第一篇发车 ...

  6. SpringBoot SpringApplication底层源码分析与自动装配

    目录 抛出问题 @SpringBootApplication注解剖析 SpringApplication类剖析 第一步:配置SpringBoot Bean来源 第二步 :自动推断SpringBoot的 ...

  7. Backbone事件模块源码分析

    事件模块Backbone.Events在Backbone中占有十分重要的位置,其他模块Model,Collection,View所有事件模块都依赖它.通过继承Events的方法来实现事件的管理,可以说 ...

  8. SpringBoot自动装配-源码分析

    1. 简介 通过源码探究SpringBoot的自动装配功能. 2. 核心代码 2.1 启动类 我们都知道SpringBoot项目创建好后,会自动生成一个当前模块的启动类.如下: import org. ...

  9. memcached(二)事件模型源码分析

    memcachedd事件模型 在memcachedd中,作者为了专注于缓存的设计,使用了libevent来开发事件模型.memcachedd的时间模型同nginx的类似,拥有一个主进行(master) ...

随机推荐

  1. 领域驱动模型DDD(三)——使用Saga管理事务

    前言 虽然一直说想写一篇关于Saga模式,在多次尝试后不得不承认这玩意儿的仿制代码真不是我一个菜鸟就能完成的,所以还是妥协般地引用现成的Eventuate Tram Saga框架(虽然我对它一直很反感 ...

  2. 使用Proftpd支持FTP/SFTP权限管控

    简介 FTP 文件传输协议,FTP由FTP服务器(存储文件)和FTP客户端(通过FTP协议访问FTP服务器上的资源)组成 传输方式 主动模式(Port) 客户端与服务器端的TCP 21端口建立连接 - ...

  3. XCTF练习题---MISC---gif

    XCTF练习题---MISC---gif flag:flag{FuN_giF} 解题步骤: 1.观察题目,下载附件 2.观察下载的附件,发现是由黑白块组成的,试着拼接二维码,好像不太对,再仔细看看感觉 ...

  4. 基础知识:CERT内部威胁定义以及四大原因

    我们从CERT的内部威胁定义中,可以分析.提取出内部威胁的关键特征,而这些特征也是内部威胁与外部威胁区别的最主要因素.通常来说,内部威胁具有以下特征: 1.透明性:攻击者来自安全边界内部,因此攻击者可 ...

  5. 下载并配置pycharm

    1.下载(推荐下载社区版) https://www.jetbrains.com/pycharm/download/#section=windows 2.配置代码编写前注释 得到这种效果: 3.设置字体 ...

  6. CPU缓存L1/L2/L3工作原理

    点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! 一.前言 在过去的几年中,计算机处理器取得了相当大的进步 ...

  7. go 语言开发2 简易数据库和web代码示例

    数据库开发示例 package dao import ( "github.com/go-xorm/xorm" "fmt" ) type UserInfo str ...

  8. SSO 方案演进

    背景介绍 随着业务与技术的发展,现今比以往任何时候都更需要单点登录 SSO 身份验证. 现在几乎每个网站都需要某种形式的身份验证才能访问其功能和内容. 随着网站和服务数量的增加,集中登录系统已成为一种 ...

  9. 基于SqlSugar的开发框架循序渐进介绍(4)-- 在数据访问基类中对GUID主键进行自动赋值处理

    我们在设计数据库表的时候,往往为了方便,主键ID一般采用字符串类型或者GUID类型,这样对于数据库表记录的迁移非常方便,而且有时候可以在处理关联记录的时候,提前对应的ID值.但有时候进行数据记录插入的 ...

  10. Spring Boot 配置 HikariCP

    HikariCP 是一个可靠的.高性能的 JDBC 连接池 本来用的 alibaba/druid,但实际并没有怎么用其内置的监控网页,然后多方调查,决定弃用 druid,替换为 HikariCP Sp ...