spring源码解读系列(八):观察者模式--spring监听器详解
一、前言
在前面的文章spring源码解读系列(七)中,我们继续剖析了spring的核心refresh()方法中的registerBeanPostProcessors(beanFactory)(完成BeanPostProcessor的注册)和initMessageSource()(为上下文初始化message源,即不同语言的消息体,国际化处理),有心的朋友顺着我们的思路继续看,发现下面开始了initApplicationEventMulticaster()(初始化事件监听多路广播器),那么什么又是多路广播器呢?这和spring中的监听器什么关系?这个和观察者模式又是什么关系?本文将带你一探究竟,理顺整个脉络。
二、观察者模式的定义
观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式,它是对象行为型模式:
观察者模式是一种对象行为型模式,其主要优点如下。
1.降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
2.目标与观察者之间建立了一套触发机制
主要缺点如下:
1.目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
2.当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。
三、jdk中的观察者模式
原理解析:
1.用户自定义类继承Observable,作为被观察者,自定义方法执行需要的业务逻辑,执行完成后,通过Observable中的notifyObservers()方法通知观察者
2.用户自定义类实现Observer接口,重写update方法,处理被观察者发生行为后触发的逻辑。
3.调用Observable中的addObserver方法,添加对应的观察者
自己实现一个简单的案例:
package edu.jiahui.weixingateway.utils;import java.util.Observable;
import java.util.Observer;/*** 定义被观察者*/
public class ExpertObservable extends Observable {public void writeBlog(){System.out.println("大佬在写博客");// 设置changed = true,表示发生了变化super.setChanged();super.notifyObservers();}}/*** 定义观察者-小明*/
class XiaoMing implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.println("小明在点赞!");}
}
/*** 定义观察者-小红*/
class XiaoHong implements Observer {@Overridepublic void update(Observable o, Object arg) {System.out.println("小红在评论!");}
}/*** 测试类*/
class TestObserver{public static void main(String[] args) {// 创建被观察者对象final ExpertObservable expertObservable = new ExpertObservable();// 添加观察者expertObservable.addObserver(new XiaoMing());expertObservable.addObserver(new XiaoHong());// 被观察者执行方法expertObservable.writeBlog();}
}
输出结果
四、spring中的观察者模式
spring中的事件监听机制,很多人也叫做监听器模式,其实是标准的观察者模式(23中设计模式中并没有监听器模式),只不过对观察者模式实现了更加的抽象化和细化,其中主要有四个角色:
ApplicationListener:事件监听器,等同于jdk中的观察者Observer,实现类继承该接口,重写onApplicationEvent方法,处理对事件发生的响应。
ApplicationEventMulticaster:事件多路广播器,等同于jdk中被观察者Observable中的obs属性,负责添加、移除、通知观察者,可以看下它的类结构,是不是很眼熟
ApplicationEvent:事件,监听器监听到事件后作出响应,类似于我们上面例子中的writeBlog()方法就是一个事件,只不过spring这里进行了抽象,抽象出了这个具体的接口,运用起来更加灵活,扩展性更强,在此进一步感叹下,源码的牛逼!
ApplicationEventPublisher:事件发布者,即事件源,产生事件的主体,等同于jdk中的被观察者Observable,我们研究spring源码中的AbstractApplicationContext实现了该接口,即是一个事件源,通过调用publishEvent方法发布事件。
Spring事件监听机制流程:
流程解释
1.流程分为两个阶段
(1)一个是启动Spring容器
(2)另外一个是我们触发事件的时候
2.核心还是事件广播器ApplicationEventMulticaster(这里实际指的是它的实现类ApplicationEventMulticaster,SimpleApplicationEventMulticaster)
3.增加监听器是在启动Spring容器时候完成的(图中紫红色的部分)。这也是Spring容器的核心位置。为防止读者在自己看源码的时候疑惑,图中我特意把两个加载linstener的过程都画出来。这两个addxxx分别是:
(1)增加普通的监听器,即通过实现ApplicationListener实现的监听器
(2)增加使用注解(@EventListener)实现的监听器
4.事件发布。这是我们写程序可触及到的一部分流程。核心是ApplicationEventPublisher。这里会首先去调用事件广播器的getApplicationListeners方法,拿到所有的监听器(由于前面启动时已经加载里所有监听器,所以这里可以拿到),然后逐个调用监听器内的方法。
五、initApplicationEventMulticaster()
掌握了上面的基本知识,我们就可以很轻松惬意的继续解读spring的源码了,顺着refresh()方法,继续解读initApplicationEventMulticaster()方法:
protected void initApplicationEventMulticaster() {// 获取当前bean工厂,一般是DefaultListableBeanFactoryConfigurableListableBeanFactory beanFactory = getBeanFactory();// 判断容器中是否存在bdName为applicationEventMulticaster的bd,也就是说自定义的事件监听多路广播器,必须实现ApplicationEventMulticaster接口if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {// 如果有,则从bean工厂得到这个bean对象this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);if (logger.isTraceEnabled()) {logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");}}else {// 如果没有,则默认采用SimpleApplicationEventMulticasterthis.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);if (logger.isTraceEnabled()) {logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");}}}
这里即是初始化一个多路广播器,为后续的发布事件作准备,原理也很简单:spring判断beanFactory中是否含有用户自定义的applicationEventMulticaster,如果有,就使用用户自定义的,否则,则使用默认的SimpleApplicationEventMulticaster。
六、onRefresh()
这个方法在spring中是个空实现,主要留给子类扩展,这里不做过多解读,在springboot中有具体的实现,用以完成创建web容器,详见org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh方法
这里完成了web容器的创建,springboot默认tomcat,感兴趣的朋友可以研究下,本人也会在后续博客中推出springboot源码解析系列,作义详细探讨。
七、registerListeners()
见名知义,注册监听器,即将目前可以获取到的所有监听器ApplicationListener,注册到多路广播器ApplicationEventMulticaster中,详见源码解析:
protected void registerListeners() {// Register statically specified listeners first.// 遍历应用程序中存在的监听器集合,并将对应的监听器添加到监听器的多路广播器中for (ApplicationListener<?> listener : getApplicationListeners()) {getApplicationEventMulticaster().addApplicationListener(listener);}// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let post-processors apply to them!// 从容器中获取所有实现了ApplicationListener接口的bd的bdName// 放入ApplicationListenerBeans集合中String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);for (String listenerBeanName : listenerBeanNames) {getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);}// Publish early application events now that we finally have a multicaster...// 此处先发布早期的监听器集合Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;this.earlyApplicationEvents = null;if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {for (ApplicationEvent earlyEvent : earlyEventsToProcess) {getApplicationEventMulticaster().multicastEvent(earlyEvent);}}}
总结为三步:
1.从AbstractApplicationContext类中的Set<ApplicationListener<?>> applicationListeners取出用户添加的监听器ApplicationListener,添加到多路广播器中
2.从spring工厂中取出符合条件的ApplicationListener,添加到多路广播器中
3.对早期添加在earlyApplicationEvents中的事件进行发布
通过debug发现,spring这里的监听器和事件earlyApplicationEvents都为空,主要是为了扩展方便,在springmvc和springboot中有具体的监听器和事件,感兴趣的可以自己研究下,我也会在后续的springmvc和springboot源码解析中带着大家学习。
spring源码解读系列(八):观察者模式--spring监听器详解相关推荐
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- Myth源码解析系列之四- 配置与启动详解
在上一篇中,我们项目所需的整个环境都已搭建完成,下面我们主要介绍项目的相关配置于启动环节 配置详解 注意: 这里事务存储我们这里采用的是 : mysql, 消息中间件选择的是:rocketmq, 其他 ...
- Alamofire源码解读系列(九)之响应封装(Response)
本篇主要带来Alamofire中Response的解读 前言 在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下.我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然, ...
- Alamofire源码解读系列(十二)之请求(Request)
本篇是Alamofire中的请求抽象层的讲解 前言 在Alamofire中,围绕着Request,设计了很多额外的特性,这也恰恰表明,Request是所有请求的基础部分和发起点.这无疑给我们一个Req ...
- Alamofire源码解读系列(十一)之多表单(MultipartFormData)
本篇讲解跟上传数据相关的多表单 前言 我相信应该有不少的开发者不明白多表单是怎么一回事,然而事实上,多表单确实很简单.试想一下,如果有多个不同类型的文件(png/txt/mp3/pdf等等)需要上传给 ...
- Spring 源码解读第七弹!bean 标签的解析
Spring 源码解读继续. 本文是 Spring 系列第八篇,如果小伙伴们还没阅读过本系列前面的文章,建议先看看,这有助于更好的理解本文. Spring 源码解读计划 Spring 源码第一篇开整! ...
- Spring源码分析系列——bean创建过程分析(三)——工厂方法创建bean
前言 spring创建bean的方式 测试代码准备 createBeanInstance()方法分析 instantiateUsingFactoryMethod()方法分析 总结 spring创建be ...
- Alamofire源码解读系列(五)之结果封装(Result)
本篇讲解Result的封装 前言 有时候,我们会根据现实中的事物来对程序中的某个业务关系进行抽象,这句话很难理解.在Alamofire中,使用Response来描述请求后的结果.我们都知道Alamof ...
- py-faster-rcnn源码解读系列
转载自: py-faster-rcnn源码解读系列(一)--train_faster_rcnn_alt_opt.py - sunyiyou9的博客 - 博客频道 - CSDN.NET http://b ...
最新文章
- Class.forName 和 ClassLoader 到底有啥区别?
- 移动硬盘提示此卷不包含可识别的文件系统数据如何恢复
- C++中getline()的用法
- 2017吉首大学新生赛
- mysql 乘法拼接字符串_【原创】利用MySQL 的GROUP_CONCAT函数实现聚合乘法
- (Spring)概述及IOC
- java监控数据库性能_Java:GraalVM数据库流性能
- Visual Studio IDE 背景色该为保护眼睛色
- 19.Delete Documents-官方文档摘录
- 三星在美国开售低价版5G手机 吸引价格敏感消费者
- Python基础知识:字符串
- 九度 1529:棋盘寻宝(递推DP)
- unix进程的环境--unix环境高级编程读书笔记
- Java实现opendir的api_api代码生成
- 优酷视频kux格式转mp4格式
- 信号与系统28(状态变量与状态方程)
- 如何0成本搭建外卖CPS返利小程序
- linux v4l2-ctl,V4L2总结
- c语言中的加减乘除字母,C 语言简单加减乘除运算
- 前端校招该考察什么?一个面试官的思考