Sping源码(八)—Spring事件驱动

观察者模式

在介绍Spring的事件驱动之前,先简单的介绍一下设计模式中的观察者模式。
在一个简单的观察者模式只需要观察者和被观察者两个元素。简单举个栗子:
在这里插入图片描述
以警察盯梢犯罪嫌疑人的栗子来说:
其中犯罪嫌疑人为被观察者元素警察和军人为观察者元素,被观察者的状态发生了改变(run),观察者收到通知并进行相应改变(追捕)。

代码

被观察者
图示中标记可以看出,可能会有多个观察者进行观察,所以会有add、remove方法,如果被观察者状态进行改变,则调用notifyObservers()方法通知所有的观察者。

public interface Observable {//收集对应的观察者到集合中void addObserver(Observer observer);//从集合中移除对应的观察者void removeObserver(Observer observer);//通知所有观察者void notifyObservers(String str);
}

被观察者实现类

public class BadMan implements Observable {List<Observer> observerList = new ArrayList<>();@Overridepublic void addObserver(Observer observer) {this.observerList.add(observer);}@Overridepublic void removeObserver(Observer observer) {this.observerList.remove(observer);}@Overridepublic void notifyObservers(String str) {System.out.println(str);for (Observer observer : observerList) {observer.apprehend();}}public void run(String str) {notifyObservers(str);}public void play(String str){System.out.println(str);}
}

观察者

public interface Observer {//抓捕方法void apprehend();
}

观察者实现类
当接到被观察者通知,做出相应的逻辑。

public class GoodMan1 implements Observer{@Overridepublic void apprehend() {System.out.println("goodman1 ---------抓捕小偷");}
}public class GoodMan2 implements Observer{@Overridepublic void apprehend() {System.out.println("goodman2------------逮捕小偷");}
}

测试

public static void main(String[] args) {GoodMan1 gm1 = new GoodMan1();GoodMan2 gm2 = new GoodMan2();BadMan bm = new BadMan();bm.addObserver(gm1);bm.addObserver(gm2);bm.run("小偷逃跑,开始追踪");bm.play("小偷在玩----不用理会");}

Spring事件驱动

Spring的事件驱动其实和上面介绍的观察者模式差不多,不过进行了更细致的划分,也更加的解耦,我们来看看Spring的事件驱动。

图解
看源码之前先来看看Spring事件驱动和传统观察者模式的区别。
在这里插入图片描述
逻辑顺序
将Spring事件驱动的每个组件串联起来执行的顺序就是。
在这里插入图片描述

源码
依然是refresh()源码主流程,此时来到了initApplicationEventMulticaster()方法。

public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.// 为上下文初始化message源,即不同语言的消息体,国际化处理,在springmvc的时候通过国际化的代码重点讲initMessageSource();// Initialize event multicaster for this context.// 初始化事件监听多路广播器initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.//向广播器中注册listenerregisterListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();}}}

initApplicationEventMulticaster
初始化多播器,如果BeanFactory不包含,则创建一个SimpleApplicationEventMulticaster多播器。

protected void initApplicationEventMulticaster() {//获取beanFactory对象ConfigurableListableBeanFactory beanFactory = getBeanFactory();// 如果包含applicationEventMulticaster,则赋值给applicationEventMulticaster变量if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {this.applicationEventMulticaster =beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);}else {//创建默认的SimpleApplicationEventMulticasterthis.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);//注册到beanFactory中beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);}}

registerListeners
向多播器中注册Listener。应用程序中自带的Lintener -> 实现了ApplicationListener的Listener -> 发布earlyApplicationEvents 事件。
此时的earlyApplicationEvents = null,所以不会进行事件处理。

	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类型的监听器,并注册到多播器中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);}}}

finishRefresh
将监听器添加到多播器后,跳过中间方法,我们直接来看事件的发布publishEvent()

protected void finishRefresh() {// Clear context-level resource caches (such as ASM metadata from scanning).clearResourceCaches();// Initialize lifecycle processor for this context.initLifecycleProcessor();// Propagate refresh to lifecycle processor first.getLifecycleProcessor().onRefresh();// Publish the final event.publishEvent(new ContextRefreshedEvent(this));// Participate in LiveBeansView MBean, if active.LiveBeansView.registerApplicationContext(this);}

事件发布
根据publishEvent方法的调用,event参数为 new ContextRefreshedEvent(),根据上面图示,此时事件源为我当前AbstractApplicationContext类进行的事件发布

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {Assert.notNull(event, "Event must not be null");// Decorate event as an ApplicationEvent if necessary// 如果事件不是ApplicationEvent,则创建一个PayloadApplicationEventApplicationEvent applicationEvent;if (event instanceof ApplicationEvent) {applicationEvent = (ApplicationEvent) event;}else {applicationEvent = new PayloadApplicationEvent<>(this, event);//如果event = nullif (eventType == null) {// 将applicationEvent转换为PayloadApplicationEvent对象象,引用其ResolvableType对象eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();}}// Multicast right now if possible - or lazily once the multicaster is initialized// 如果可能的话,现在就进行组播——或者在组播初始化后延迟// earlyApplicationEvents:在多播程序设置之前发布的ApplicationEvent// 如果earlyApplicationEvents不为 null,这种情况只在上下文的多播器还没有初始化的情况下才会成立,会将applicationEvent// 添加到earlyApplicationEvents保存起来,待多博器初始化后才继续进行多播到适当的监听器if (this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);}else {getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}// Publish event via parent context as well...// 如果父上下文不为空,则通过父上下文发布事件if (this.parent != null) {if (this.parent instanceof AbstractApplicationContext) {((AbstractApplicationContext) this.parent).publishEvent(event, eventType);}else {this.parent.publishEvent(event);}}}
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {// 如果eventType不为null就引用eventType;否则将event转换为ResolvableType对象再引用ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));//获取当前多播器的任务线程池Executor executor = getTaskExecutor();//根据给定事件和类型匹配的应用监听器集合// 遍历所有监听器for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {if (executor != null) {//使用executor回调listener的onApplicationEvent方法,传入eventexecutor.execute(() -> invokeListener(listener, event));}else {//回调listener的onApplicationEvent方法,传入eventinvokeListener(listener, event);}}}

流程图
在这里插入图片描述

SpringBoot

事件驱动在Spring中没有明显的处理过程,我们结合SpringBoot来一起验证一下。从SpringBoot的启动run()方法开始。

spring.factories
首先我们可以看到配置文件中准备的一些Listener
在这里插入图片描述
run
我们的run()方法中,首先通过SpringApplicationRunListeners对Spring自带的Listener进行事件的发布处理。

public ConfigurableApplicationContext run(String... args) {StopWatch stopWatch = new StopWatch();stopWatch.start();ConfigurableApplicationContext context = null;configureHeadlessProperty();SpringApplicationRunListeners listeners = getRunListeners(args);listeners.starting();try {// 省略部分代码....refreshContext(context);// 省略部分代码....}return context;}

创建SpringApplicationRunListener对象并获取自带Listener集合。

//获取SpringApplicationRunListeners对象
private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };//创建SpringApplicationRunListeners对象并返回return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));}private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {ClassLoader classLoader = getClassLoader();// Use names and ensure unique to protect against duplicates//loadFactoryNames会加载spring.factories文件,并转换成key , value的Map放入缓存中//如上图所示,根据key 来获取 ListenerSet<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);AnnotationAwareOrderComparator.sort(instances);return instances;}

在这里插入图片描述
事件发布
listeners.starting() 底层会通过 initialMulticaster 循环遍历对满足条件的Listener 进行事件发布通知,底层同样执行listener.onApplicationEvent方法。

public void starting() {this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));}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的加载

多播器可能有多个,其中initialMulticaster是一个有别于上文提到的applicationEventMulticaster。但作用都是相同的,遍历Listener过滤出符合条件的监听器进行事件处理。

初始化
initialMulticaster变量是随着EventPublishingRunListener类的加载而进行的初始化,而EventPublishingRunListener的创建也是根据spring.factories的加载而生成的。

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

在这里插入图片描述

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,ClassLoader classLoader, Object[] args, Set<String> names) {List<T> instances = new ArrayList<>(names.size());for (String name : names) {try {Class<?> instanceClass = ClassUtils.forName(name, classLoader);Assert.isAssignable(type, instanceClass);Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);T instance = (T) BeanUtils.instantiateClass(constructor, args);instances.add(instance);}catch (Throwable ex) {throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);}}return instances;}

在这里插入图片描述

同一个监听器的不同事件处理

上面有提到监听器会根据不同的事件作出不同的处理。以ConfigFileApplicationListener 为例,onApplicationEvent方法中会根据event的类型不同,而有不同的实现逻辑。

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {@Overridepublic void onApplicationEvent(ApplicationEvent event) {if (event instanceof ApplicationEnvironmentPreparedEvent) {onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);}if (event instanceof ApplicationPreparedEvent) {onApplicationPreparedEvent(event);}}
}	

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/16488.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【启程Golang之旅】基本变量与类型讲解

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

【JVM】内存区域划分 | 类加载的过程 | 双亲委派机制 | 垃圾回收机制

文章目录 JVM一、内存区域划分1.方法区&#xff08;1.7之前&#xff09;/ 元数据区&#xff08;1.8开始&#xff09;2.堆3.栈4.程序计数器常见面试题&#xff1a; 二、类加载的过程1.类加载的基本流程1.加载2.验证3.准备4.解析5.初始化 2.双亲委派模型类加载器找.class文件的过…

[JDK工具-5] jinfo jvm配置信息工具

文章目录 1. 介绍2. 打印所有的jvm标志信息 jinfo -flags pid3. 打印指定的jvm参数信息 jinfo -flag InitialHeapSize pid4. 启用或者禁用指定的jvm参数 jinfo -flags [|-]HeapDumpOnOutOfMemoryError pid5. 打印系统参数信息 jinfo -sysprops pid6. 打印以上所有配置信息 jinf…

WordPress安装memcached提升网站速度

本教程使用环境为宝塔 第一步、服务器端安装memcached扩展 在网站使用的php上安装memcached扩展 第二步&#xff1a;在 WordPress 网站后台中&#xff0c;安装插件「Memcached Is Your Friend」 安装完成后启用该插件&#xff0c;在左侧工具-中点击Memcached 查看是否提示“U…

Leetcode - 398周赛

目录 一&#xff0c;3151. 特殊数组 I 二&#xff0c;3152. 特殊数组 II 三&#xff0c;3153. 所有数对中数位不同之和 四&#xff0c;3154. 到达第 K 级台阶的方案数 一&#xff0c;3151. 特殊数组 I 本题就是判断一个数组是否是奇偶相间的&#xff0c;如果是&#xff0c;…

Linux下的调试器 : gdb指令详解

&#x1fa90;&#x1fa90;&#x1fa90;欢迎来到程序员餐厅&#x1f4ab;&#x1f4ab;&#x1f4ab; 主厨&#xff1a;邪王真眼 主厨的主页&#xff1a;Chef‘s blog 所属专栏&#xff1a;青果大战linux 总有光环在陨落&#xff0c;总有新星在闪烁 gdb是什么 gdn是linu…

开源大模型与闭源大模型,你更看好哪一方?

开源大模型与闭源大模型&#xff0c;你更看好哪一方&#xff1f; 简介&#xff1a;评价一个AI模型“好不好”“有没有发展”&#xff0c;首先就躲不掉“开源”和“闭源”两条发展路径。对于这两条路径&#xff0c;你更看好哪一种呢&#xff1f; 1.方向一&#xff1a;数据隐私 …

英伟达的GPU(3)

上节内容&#xff1a;英伟达的GPU(2) (qq.com) 书接上文&#xff0c;上文我们讲到CUDA编程体系和硬件的关系&#xff0c;也留了一个小问题CUDA core以外的矩阵计算能力是咋提供的 本节介绍一下Tensor Core 上节我们介绍了CUDA core&#xff0c;或者一般NPU&#xff0c;CPU执行…

pyqt QMainWindow菜单栏

pyqt QMainWindow菜单栏 pyqt QMainWindow菜单栏效果代码 pyqt QMainWindow菜单栏 QMainWindow 是 PyQt中的一个核心类&#xff0c;它提供了一个主应用程序窗口&#xff0c;通常包含菜单栏、工具栏、状态栏、中心窗口&#xff08;通常是一个 QWidget 或其子类&#xff09;等。…

【数据结构/C语言】深入理解 双向链表

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;数据结构与算法 在阅读本篇文章之前&#xff0c;您可能需要用到这篇关于单链表详细介绍的文章 【数据结构/C语言】深入理解 单链表…

[vue error] vue3中使用同名简写报错 ‘v-bind‘ directives require an attribute value

错误详情 错误信息 ‘v-bind’ directives require an attribute value.eslintvue/valid-v-bind 错误原因 默认情况下&#xff0c;ESLint 将同名缩写视为错误。此外&#xff0c;Volar 扩展可能需要更新以支持 Vue 3.4 中的新语法。 解决方案 更新 Volar 扩展 安装或更新 …

java人口老龄化社区服务与管理平台源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的人口老龄化社区服务与管理平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 人口老龄化…

Elasticsearch的Index sorting 索引预排序会导致索引数据的移动吗?

索引预排序可以确保索引数据按照指定字段的指定顺序进行存储&#xff0c;这样在查询的时候&#xff0c;如果固定使用这个字段进行排序就可以加快查询效率。 我们知道数据写入的过程中&#xff0c;如果需要确保数据有序&#xff0c;可能需要在原数据的基础上插入新的数据&#…

vue实现页面渲染时候执行某需求

1. 前言 在之前的项目中&#xff0c;需要实现一个监控token是否过期从而动态刷新token的功能&#xff0c;然而在登录成功后创建的监控器会在浏览器刷新点击或者是通过导航栏输入网址时销毁... 2. 试错 前前后后始过很多方法&#xff0c;在这里就记录一下也许也能为各位读者排…

【每日力扣】84. 柱状图中最大的矩形 与 295. 数据流的中位数

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害 84. 柱状图中最大的矩形 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为…

redis6.2.7 搭建一主多从

1、集群规划 节点端口角色192.168.137.1026379master192.168.137.1026380slave192.168.137.1036381slave 2、伪集群搭建 2.1 创建fake_cluster 目录存放 公共配置文件 # 进入redis目录 cd /app/apps/redis-6.2.7# 创建存放伪集群的目录 mkdir fake_cluster#复制redis.conf到…

DTC 2024回顾丨云和恩墨重塑数据库内核技术,革新企业降本增效之道

在数字化浪潮席卷全球的当下&#xff0c;关系型数据库作为市场主导力量的地位依然稳固。然而&#xff0c;面对新兴数据库与服务形态的挑战&#xff0c;以及企业日益强烈的降本增效需求&#xff0c;数据库技术的发展必须紧跟时代步伐&#xff0c;充分发挥资源效能以提升企业竞争…

【机器学习300问】99、多通道卷积神经网络在卷积操作时有哪些注意事项?

一、多通道卷积神经网络示例 还是以图像处理为例&#xff0c;如果你的目标不仅是分析灰度图像特性&#xff0c;还打算捕捉RGB彩色图像的特征。如下图&#xff0c;当面对一张66像素的彩色图像时&#xff0c;提及的“3”实际上是指红、绿、蓝三种颜色通道&#xff0c;形象地说&am…

书生·浦语第二期-笔记2

课程链接&#xff1a;https://github.com/InternLM/Tutorial/tree/camp2 视频地址&#xff1a;轻松玩转书生浦语大模型趣味Demo_哔哩哔哩_bilibili 大模型及InternLM介绍 大模型&#xff1a;人工智能领域中参数数量巨大、拥有庞大计算能力和参数规模的模型 特点&#xff1a…