使用spring Boot已经快1年多了,期间一直想点开springboot源码查看,但由于种种原因一直未能如愿(主要是人类的惰性。。。),今天就拿springboot 的监听事件祭刀。

springboot 中常用的事件监听主要有ApplicationStartedEvent,ApplicationEnviromentPreparedEvent,ApplicationPreparedEvent,ApplicationStoppedEvent等。用于监听springboot生命周期中的各种事件。

说到监听都会不由自主想到观察者模式,springboot的事件监听实现也没出意外,它的实现机制也是一个典型的观察者模式,只不过稍微复杂点罢了。springboot将观察者放入一个list中委托

SimpleApplicationEventMulticaster管理。这里有个有意思的地方,srpingboot在实例化SpringApplicationRunListener 时并没有通过正常的途径(比如new,或者其他实例化方法),而是将接口的实现类交由

SpringFactoriesLoader去 META-INF/spring.factories 下装载:

public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
try {
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
List<String> result = new ArrayList<String>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
String factoryClassNames = properties.getProperty(factoryClassName);
result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
}
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +
"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

这里通过classLoader的getResources方法将所有的META-INF/spring.factories资源都装载进来。

springboot为什么要不惜花费这么大代价去装载一个接口的实现呢,稍微分析一下还是很清晰的,首先,将接口和实现分离,这是典型的依赖注入,如果从单个应用程序来看,大不必这样。关键就在SpringFactoriesLoader里,如上所说,SpringFactoriesLoader是将所有的配置都装载进来,也就是说springboot在为以后的扩展留了条后路,当有新的jar包(意味着又升级啦)加进来,并且需要添加新的的监听事件类型时,就不用修改原有的代码。这就是传说中的对修改关闭对扩展开放的典型例子。

接着说,那么SpringFactoriesLoader装载的实例到底是什么呢, EventPublishingRunListener!

这货也就是个中间类,承接了事件的管理和事件的广播,正真的主角是SimpleApplicationEventMulticaster类,该类的实例在EventPublishingRunListener实例化时被创建出来。

里面有个ListenerRetriever内部类,这个类有两个作用,一个default,SimpleApplicationEventMulticaster初始化时被创建,含有所有的观察者,并作为各种操作的同步锁,还有就是将观察者分类,分类的依据有两个,

一个是事件类型eventType,还有一个是事件原始对象类型sourceType,sourceType是每个事件都会有的,这里是applicationContext。,分类好后,会在事件发生时由最上层SpringApplication发起通知,最后由SimpleApplicationEventMulticaster对象分别调用每个具体的观察者:

protected void invokeListener(ApplicationListener listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
listener.onApplicationEvent(event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || msg.startsWith(event.getClass().getName())) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
Log logger = LogFactory.getLog(getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
}

至此,一个完整的从事件注册到事件出发再到事件执行完毕的流程结束。山路十八弯,沿途风景多,细细品味还是会有一番收获的。这当中仍有许多细节未能一一道出,比如hashmap的hashCode的重写分析,比如同步锁的使用原因等等。

转载请注明出处。

spring boot 源码赏析之事件监听的更多相关文章

  1. (转)spring boot实战(第三篇)事件监听源码分析

    原文:http://blog.csdn.net/liaokailin/article/details/48194777 监听源码分析 首先是我们自定义的main方法: package com.lkl. ...

  2. Spring Boot源码分析-配置文件加载原理

    在Spring Boot源码分析-启动过程中我们进行了启动源码的分析,大致了解了整个Spring Boot的启动过程,具体细节这里不再赘述,感兴趣的同学可以自行阅读.今天让我们继续阅读源码,了解配置文 ...

  3. Spring Boot源码分析-启动过程

    Spring Boot作为目前最流行的Java开发框架,秉承"约定优于配置"原则,大大简化了Spring MVC繁琐的XML文件配置,基本实现零配置启动项目. 本文基于Spring ...

  4. 曹工说Spring Boot源码(21)-- 为了让大家理解Spring Aop利器ProxyFactory,我已经拼了

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  5. 精尽Spring Boot源码分析 - SpringApplication 启动类的启动过程

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  6. 精尽Spring Boot源码分析 - 支持外部 Tomcat 容器的实现

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  7. 精尽Spring Boot源码分析 - 配置加载

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  8. 精尽Spring Boot源码分析 - 日志系统

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

  9. Spring Boot源码中模块详解

    Spring Boot源码中模块详解 一.源码 spring boot2.1版本源码地址:https://github.com/spring-projects/spring-boot/tree/2.1 ...

随机推荐

  1. Docker深入浅出系列 | Docker Compose多容器实战

    目录 前期准备 Docker Compose是什么 为什么要用Docker Compose Docker Compose使用场景 Docker Compose安装 Compose Yaml文件结构 C ...

  2. SQL Server 最小日志记录

    SQL Server之所以记录事务日志,首要目的是为了把失败或取消的操作还原到最原始的状态,但是,并不是所有的操作都需要完全记录事务日志,比如,在一个空表上放置排他锁,把大量的数据插入到该空表中.即使 ...

  3. 【Python3】HTML基础

    [web前端]HTML基础 一.BS模式 BS(Browser-Server)模式:顾名思义为浏览器-服务器的意思,对比的话类似我们PC上面浏览器使用的产品即为BS模式产品,例如google doc. ...

  4. 前端每日实战:92# 视频演示如何用纯 CSS 创作一颗逼真的土星

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/EpbaQX 可交互视频 此视频是可 ...

  5. 如何在普通的元素上实现enter键的绑定

    在做登录页面时候,通常当用户输入账号密码后直接按enter键就触发登录按钮了. 如果是input标签,vue中可以绑定按键修饰符,但是如果是其它标签呢.我的做法如下: document.querySe ...

  6. LeetCode 154.Find Minimum in Rotated Sorted Array II(H)(P)

    题目: Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. ( ...

  7. angular - collapse--TemplateRef

    用的ant  collapse组件 需要自定义Header 请忽略前面的dw... 主要是TemplateRef<void> 查了半天.可以实现效果,直接上代码,我也不懂为什么 反正实现了 ...

  8. 最简单易懂的实现CRC16校验

    public String getCRC16(byte[] bytes) { //CRC寄存器全为1 int CRC = 0x0000ffff; //多项式校验值 int POLYNOMIAL = 0 ...

  9. 量子计算机编程(二)——QPU基础函数

    第二部分主要是QPU的基础功能,第一部分就像是我们有了哪些基本的语句,第二部分就是我们能写一些简单基础的函数,一些小模块,第三部分就是他的应用了. 先来看一下一个简单量子应用的结构: 第一步,将量子态 ...

  10. 3DGIS与BIM集成集成技术及铁路桥梁可视化系统

    3DGIS与BIM的集成技术 3DGIS与BIM的集成技术包括2部分:一是将Revit软件生成的BIM针对3DGIS的快速无损格式转换,这种转换包括几何信息(如形状.位置等信息)和属性信息(如建筑信息 ...