SpringBoot扩展点之一:SpringApplicationRunListener
三种监听器的关系
ApplicationListener、SpringApplicationRunListeners、SpringApplicationRunListener的关系:
- SpringApplicationRunListeners类和SpringApplicationRunListener类是SpringBoot中新增的类。ApplicationListener是spring中框架的类。
- 在SpringBoot(SpringApplication类)中,使用SpringApplicationRunListeners、SpringApplicationRunListener来间接调用ApplicationListener。
- SpringApplicationRunListeners是SpringApplicationRunListener的封装,SpringApplicationRunListeners中包含多个SpringApplicationRunListener,是为了批量执行的封装,SpringApplicationRunListeners与SpringApplicationRunListener生命周期相同,调用每个周期的各个SpringApplicationRunListener。然后广播相应的事件到Spring框架的ApplicationListener。
通过上面的3个特点可以看出SpringApplicationRunListener就是一个ApplicationListener的代理。springboot启动的几个主要过程的监听通知都是通过他来进行回调。
SpringApplicationRunListener
从命名我们就可以知道它是一个监听者,那纵观整个启动流程我们会发现,它其实是用来在整个启动流程中接收不同执行点事件通知的监听者,SpringApplicationRunListener接口规定了SpringBoot的生命周期,在各个生命周期广播相应的事件,调用实际的ApplicationListener类。
源码如下:
public interface SpringApplicationRunListener {
//刚执行run方法时
void started();
//环境建立好时候
void environmentPrepared(ConfigurableEnvironment environment);
//上下文建立好的时候
void contextPrepared(ConfigurableApplicationContext context);
//上下文载入配置时候
void contextLoaded(ConfigurableApplicationContext context);
//上下文刷新完成后,run方法执行完之前
void finished(ConfigurableApplicationContext context, Throwable exception);
}
它定义了5个步骤:
- started(run方法执行的时候立马执行;对应事件的类型是ApplicationStartedEvent):通知监听器,SpringBoot开始执行
- environmentPrepared(ApplicationContext创建之前并且环境信息准备好的时候调用;对应事件的类型是ApplicationEnvironmentPreparedEvent):通知监听器,Environment准备完成
- contextPrepared(ApplicationContext创建好并且在source加载之前调用一次;没有具体的对应事件):通知监听器,ApplicationContext已经创建并初始化完成
- contextLoaded(ApplicationContext创建并加载之后并在refresh之前调用;对应事件的类型是ApplicationPreparedEvent):通知监听器,ApplicationContext已经完成IoC配置价值
- finished(run方法结束之前调用;对应事件的类型是ApplicationReadyEvent或ApplicationFailedEvent):通知监听器,SpringBoot启动完成
实现类 EventPublishingRunListener类
EventPublishingRunListener类 实现了SpringApplicationRunListener,它具有广播事件的功能。
构造函数
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
private final SpringApplication application;
private final String[] args;
private final SimpleApplicationEventMulticaster initialMulticaster;
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);
}
}
//...
从上面代码可以看出,它使用了Spring广播器SimpleApplicationEventMulticaster。它把监听的过程封装成了SpringApplicationEvent事件并让内部属性(属性名为multicaster)ApplicationEventMulticaster接口的实现类SimpleApplicationEventMulticaster广播出去,广播出去的事件对象会被SpringApplication中的listeners属性进行处理。
EventPublishingRunListener的initialMulticaster成员变量SimpleApplicationEventMulticaster,以及它的multicastEvent()和invokeListener()方法
initialMulticaster是在构造函数中初始化的,见上面的代码片段。
源码如下:
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
} @Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
和invokeListener()方法,其参数是ApplicationListener和ApplicationEvent
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);
}
}
EventPublishingRunListener的5个方法:
首先看下starting方法:
public void starting() {
this.initialMulticaster
.multicastEvent(new ApplicationStartedEvent(this.application, this.args));
}
ApplicationStartingEvent构造方法中传递了一个SpringApplication对象和args参数。一直传递到了父类EventObject,将SpringApplication对象存放在source变量中。
EventObject类结构。getSource()方法得到的就是SpringApplication对象。

所以说SpringApplicationRunListener和ApplicationListener之间的关系是通过ApplicationEventMulticaster广播出去的SpringApplicationEvent所联系起来的。更多的spring事件知识见《ApplicationEvent事件机制源码分析》

如何扩展
对于开发者来说,基本没有什么常见的场景要求我们必须实现一个自定义的SpringApplicationRunListener,即使是SpringBoot中也只默认实现了一个org.springframework.boot.context.eventEventPublishingRunListener, 用来在SpringBoot的整个启动流程中的不同时间点发布不同类型的应用事件(SpringApplicationEvent)。那些对这些应用事件感兴趣的ApplicationListener可以接受并处理(这也解释了为什么在SpringApplication实例化的时候加载了一批ApplicationListener,但在run方法执行的过程中并没有被使用)。
如果我们真的在实际场景中自定义实现SpringApplicationRunListener,有一个点需要注意:任何一个SpringApplicationRunListener实现类的构造方法都需要有两个构造参数,一个参数的类型就是我们的org.springframework.boot.SpringApplication,另外一个参数就是args参数列表的String[]:
package com.dxz.controller; import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringApplicationRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment; public class SampleSpringApplicationRunListener implements SpringApplicationRunListener {
private final SpringApplication application;
private final String[] args; public SampleSpringApplicationRunListener(SpringApplication sa, String[] args) {
this.application = sa;
this.args = args;
} @Override
public void starting() {
System.out.println("自定义starting");
} @Override
public void environmentPrepared(ConfigurableEnvironment environment) {
System.out.println("自定义environmentPrepared");
} @Override
public void contextPrepared(ConfigurableApplicationContext context) {
System.out.println("自定义contextPrepared");
} @Override
public void contextLoaded(ConfigurableApplicationContext context) {
System.out.println("自定义contextLoaded");
} @Override
public void finished(ConfigurableApplicationContext context, Throwable exception) {
System.out.println("自定义finished");
}
}
接着,我们还要满足SpringFactoriesLoader的约定,在当前SpringBoot项目的classpath下新建META-INF目录,并在该目录下新建spring.fatories文件,文件内容如下:
org.springframework.boot.SpringApplicationRunListener=\
com.dxz.SampleSpringApplicationRunListener
SpringBoot扩展点之一:SpringApplicationRunListener的更多相关文章
- SpringBoot扩展SpringMVC自动配置
SpringBoot中自动配置了 ViewResolver(视图解析器) ContentNegotiatingViewResolver(组合所有的视图解析器) 自动配置了静态资源文件夹.静态首页.fa ...
- SpringBoot扩展点之二:ApplicationRunner和CommandLineRunner的扩展
CommandLineRunner并不是Spring框架原有的概念,它属于SpringBoot应用特定的回调扩展接口: public interface CommandLineRunner { /** ...
- springboot:扩展类型转换器
需求:提交一个字符串到后端的java.sql.Time类型,就报错了: Failed to convert property value of type [java.lang.String] to r ...
- SpringBoot扩展点之三:SpringBootServletInitializer扩展
SpringBootServletInitializer 熟悉了SpringApplication的原理之后,我们再来了解SpringBootServletInitializer的原理就比较容易了. ...
- (转)springboot应用启动原理(二) 扩展URLClassLoader实现嵌套jar加载
转:https://segmentfault.com/a/1190000013532009 在上篇文章<springboot应用启动原理(一) 将启动脚本嵌入jar>中介绍了springb ...
- SpringBoot:扩展SpringMVC、定制首页、国际化
目录 扩展使用SpringMVC 如何扩展SpringMVC 为何这么做会生效(原理) 全面接管SpringMVC 首页实现 页面国际化 SpringBoot扩展使用SpringMVC.使用模板引擎定 ...
- SpringBoot事件监听机制源码分析(上) SpringBoot源码(九)
SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplicat ...
- 这一次搞懂SpringBoot核心原理(自动配置、事件驱动、Condition)
@ 目录 前言 正文 启动原理 事件驱动 自动配置原理 Condition注解原理 总结 前言 SpringBoot是Spring的包装,通过自动配置使得SpringBoot可以做到开箱即用,上手成本 ...
- 微服务springboot视频最新SpringBoot2.0.3版本技术视频教程【免费学习】
超火爆的springboot微服务技术怎么学,看这里,springboot超详细的教程↓↓↓↓↓↓https://ke.qq.com/course/179440?tuin=9b386640 01.sp ...
随机推荐
- Gin-Go学习笔记八:Gin-Web框架 常用的包
常用的包 1> 在java,.net,php,node.js等语言常常会使用到包的概念.包的使用,可以加快项目的进度的开发,以及更好的实现项目的效果.我在网上查到了包的作用如下: 1.包 ...
- thinkphp路由配置route.php
路由设置配置 打开route.php 引入Route控制器类(use think\Route;) 设置路由--> Route::rule('路由表达式','路由地址','请求类型','路由参 ...
- Go 语言基础语法-Go
Go 标记 Go 程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号.如以下 GO 语句由 6 个标记组成: fmt.Println("Hello, World!") ...
- Mysql-5.5版本搭建实例的部分库的从库
由于业务需要在Mysql实例中创建部分库的从库,已有的Mysql实例的版本是mysql-5.5.49,是一个非常老的版本. 本文档涉及到服务器中运行多实例和构建实例中部分库的从库. 服务器 mysql ...
- AIX运维常用命令
目前传统的磁盘管理仍有不足:如果下Unix系统中的存储容量需要扩展,文件系统就必须停止运行,然后通过重构分区的手段来进行分区和文件系统的扩容.一般采用的方法是先备份该文件系统并删除其所在的分区,然后重 ...
- php静态化介绍
1.动态URL地址设置成静态形式http://state.com/index.php?c=play&a=index&id=16267 ------>http://state.co ...
- iview admin动态路由实现
参考 https://blog.csdn.net/weixin_41538490/article/details/93749942
- java static学习
原创,转载请注明来源sogeisetsu的博客园 static,在类里面定义公共的属性,它可以统一修改,并只占一个内存.从而达到方便修改和少占内存的目的 先放上代码,您可以先越过代码,看后面的讲解内容 ...
- 逆向破解之160个CrackMe —— 023-024
CrackMe —— 023 160 CrackMe 是比较适合新手学习逆向破解的CrackMe的一个集合一共160个待逆向破解的程序 CrackMe:它们都是一些公开给别人尝试破解的小程序,制作 c ...
- 栈 队列 hash表 堆 算法模板和相关题目
什么是栈(Stack)? 栈(stack)是一种采用后进先出(LIFO,last in first out)策略的抽象数据结构.比如物流装车,后装的货物先卸,先转的货物后卸.栈在数据结构中的地位很重要 ...