当前位置: 首页 > news >正文

米方科技网站建设/中国企业网络营销现状

米方科技网站建设,中国企业网络营销现状,建设银行官方网站注册,网页版微信二维码登录方法SpringBoot 源码解析4:事件监听器 1. 初始化监听器2. 创建事件发布器 SpringApplicationRunListeners3. 事件分发流程3.1 SimpleApplicationEventMulticaster#multicastEvent3.2 获取监听器 AbstractApplicationEventMulticaster#getApplicationListeners3.3 Abstra…

SpringBoot 源码解析4:事件监听器

    • 1. 初始化监听器
    • 2. 创建事件发布器 SpringApplicationRunListeners
    • 3. 事件分发流程
      • 3.1 SimpleApplicationEventMulticaster#multicastEvent
      • 3.2 获取监听器 AbstractApplicationEventMulticaster#getApplicationListeners
      • 3.3 AbstractApplicationEventMulticaster#retrieveApplicationListeners
      • 3.4 AbstractApplicationEventMulticaster#supportsEvent

1. 初始化监听器

SpringBoot启动需要环境配置,在这个步骤之前Spring容器并没有刷新,无法获取自定义的监听器。所以在SpringApplication的构造器中,在spring.factories文件中定义了监听器。当然用户也能通过@Component定义监听器监听ApplicationStartedEvent事件。

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));this.webApplicationType = WebApplicationType.deduceFromClasspath();setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));this.mainApplicationClass = deduceMainApplicationClass();
}

在创建SpringApplication构造器的时候,通过ApplicationListener类名对应的key获取到监听器的类名,并且实例化,保存到SprngApplication.listeners

2. 创建事件发布器 SpringApplicationRunListeners

在SpringApplication#run方法中,获取到了SpringApplicationRunListeners,并且对不同的事件进行了分发。

//SpringApplication#getRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}//EventPublishingRunListener#EventPublishingRunListener
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);}
}//AbstractApplicationEventMulticaster#addApplicationListener
public void addApplicationListener(ApplicationListener<?> listener) {synchronized (this.retrievalMutex) {// Explicitly remove target for a proxy, if registered already,// in order to avoid double invocations of the same listener.Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);if (singletonTarget instanceof ApplicationListener) {this.defaultRetriever.applicationListeners.remove(singletonTarget);}this.defaultRetriever.applicationListeners.add(listener);this.retrieverCache.clear();}
}

实例化SpringApplicationRunListener对应的类名,也就是EventPublishingRunListener。其实真正负责事件发布的是initialMulticaster(SimpleApplicationEventMulticaster),从springApplication中拿到已经初始化完毕的监听器,放入到initialMulticaster,以便回调监听器。

	void starting() {for (SpringApplicationRunListener listener : this.listeners) {listener.starting();}}void environmentPrepared(ConfigurableEnvironment environment) {for (SpringApplicationRunListener listener : this.listeners) {listener.environmentPrepared(environment);}}void contextPrepared(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {listener.contextPrepared(context);}}void contextLoaded(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {listener.contextLoaded(context);}}void started(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {listener.started(context);}}void running(ConfigurableApplicationContext context) {for (SpringApplicationRunListener listener : this.listeners) {listener.running(context);}}void failed(ConfigurableApplicationContext context, Throwable exception) {for (SpringApplicationRunListener listener : this.listeners) {callFailedListener(listener, context, exception);}}

可以看到,SpringApplicationRunListeners中分发了很多事件,我们就拿environmentPrepared举例。

3. 事件分发流程

3.1 SimpleApplicationEventMulticaster#multicastEvent

@Override
public void multicastEvent(ApplicationEvent event) {multicastEvent(event, resolveDefaultEventType(event));
}@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);}}
}//回调监听器
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {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;}}
}
  1. 解析事件的类型。
  2. 获取到监听器,这里需要通过不同的事件类型去匹配,不同的监听器处理不同的事件。
  3. 这里可配置线程池执行器Executor,如果有Executor,那么就通过线程的方式去回调监听器onApplicationEvent方法

3.2 获取监听器 AbstractApplicationEventMulticaster#getApplicationListeners

protected Collection<ApplicationListener<?>> getApplicationListeners(ApplicationEvent event, ResolvableType eventType) {Object source = event.getSource();Class<?> sourceType = (source != null ? source.getClass() : null);ListenerCacheKey cacheKey = new ListenerCacheKey(eventType, sourceType);// Quick check for existing entry on ConcurrentHashMap...ListenerRetriever retriever = this.retrieverCache.get(cacheKey);if (retriever != null) {return retriever.getApplicationListeners();}if (this.beanClassLoader == null ||(ClassUtils.isCacheSafe(event.getClass(), this.beanClassLoader) &&(sourceType == null || ClassUtils.isCacheSafe(sourceType, this.beanClassLoader)))) {// Fully synchronized building and caching of a ListenerRetrieversynchronized (this.retrievalMutex) {retriever = this.retrieverCache.get(cacheKey);if (retriever != null) {return retriever.getApplicationListeners();}retriever = new ListenerRetriever(true);Collection<ApplicationListener<?>> listeners =retrieveApplicationListeners(eventType, sourceType, retriever);this.retrieverCache.put(cacheKey, retriever);return listeners;}}else {// No ListenerRetriever caching -> no synchronization necessaryreturn retrieveApplicationListeners(eventType, sourceType, null);}
}

先从缓存中获取,缓存有,就直接返回;缓存没有,就通过retrieveApplicationListeners返回监听器。缓存key为事件类型ApplicationEnvironmentPreparedEvent和springApplication。

3.3 AbstractApplicationEventMulticaster#retrieveApplicationListeners

private Collection<ApplicationListener<?>> retrieveApplicationListeners(ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable ListenerRetriever retriever) {List<ApplicationListener<?>> allListeners = new ArrayList<>();Set<ApplicationListener<?>> listeners;Set<String> listenerBeans;synchronized (this.retrievalMutex) {listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);}// Add programmatically registered listeners, including ones coming// from ApplicationListenerDetector (singleton beans and inner beans).for (ApplicationListener<?> listener : listeners) {if (supportsEvent(listener, eventType, sourceType)) {if (retriever != null) {retriever.applicationListeners.add(listener);}allListeners.add(listener);}}// Add listeners by bean name, potentially overlapping with programmatically// registered listeners above - but here potentially with additional metadata.if (!listenerBeans.isEmpty()) {ConfigurableBeanFactory beanFactory = getBeanFactory();for (String listenerBeanName : listenerBeans) {try {if (supportsEvent(beanFactory, listenerBeanName, eventType)) {ApplicationListener<?> listener =beanFactory.getBean(listenerBeanName, ApplicationListener.class);if (!allListeners.contains(listener) && supportsEvent(listener, eventType, sourceType)) {if (retriever != null) {if (beanFactory.isSingleton(listenerBeanName)) {retriever.applicationListeners.add(listener);}else {retriever.applicationListenerBeans.add(listenerBeanName);}}allListeners.add(listener);}}else {// Remove non-matching listeners that originally came from// ApplicationListenerDetector, possibly ruled out by additional// BeanDefinition metadata (e.g. factory method generics) above.Object listener = beanFactory.getSingleton(listenerBeanName);if (retriever != null) {retriever.applicationListeners.remove(listener);}allListeners.remove(listener);}}catch (NoSuchBeanDefinitionException ex) {// Singleton listener instance (without backing bean definition) disappeared -// probably in the middle of the destruction phase}}}AnnotationAwareOrderComparator.sort(allListeners);if (retriever != null && retriever.applicationListenerBeans.isEmpty()) {retriever.applicationListeners.clear();retriever.applicationListeners.addAll(allListeners);}return allListeners;
}
  1. 循环所有的listeners,调用supportsEvent方法判断listener是否支持当前的事件,支持就放入retriever里面。
  2. listenerBeans原理一样,只是从Spring容器中拿到所属的bean而已。

3.4 AbstractApplicationEventMulticaster#supportsEvent

这是真正判断事件与监听器匹配关系的方法

protected boolean supportsEvent(ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?(GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
}
  1. 当前的监听器是否属于GenericApplicationListener 类型,不是就包装成GenericApplicationListenerAdapter。
public GenericApplicationListenerAdapter(ApplicationListener<?> delegate) {Assert.notNull(delegate, "Delegate listener must not be null");this.delegate = (ApplicationListener<ApplicationEvent>) delegate;this.declaredEventType = resolveDeclaredEventType(this.delegate);
}
//resolveDeclaredEventType 解析监听器中声明的泛型类型
@Nullable
private static ResolvableType resolveDeclaredEventType(ApplicationListener<ApplicationEvent> listener) {ResolvableType declaredEventType = resolveDeclaredEventType(listener.getClass());if (declaredEventType == null || declaredEventType.isAssignableFrom(ApplicationEvent.class)) {Class<?> targetClass = AopUtils.getTargetClass(listener);if (targetClass != listener.getClass()) {declaredEventType = resolveDeclaredEventType(targetClass);}}return declaredEventType;
}@Nullable
static ResolvableType resolveDeclaredEventType(Class<?> listenerType) {ResolvableType eventType = eventTypeCache.get(listenerType);if (eventType == null) {eventType = ResolvableType.forClass(listenerType).as(ApplicationListener.class).getGeneric();eventTypeCache.put(listenerType, eventType);}return (eventType != ResolvableType.NONE ? eventType : null);
}

值得注意的是,在GenericApplicationListenerAdapter构造器中,resolveDeclaredEventType方法解析了监听器中声明的泛型。方便后面将监听器中声明的泛型与事件的类型进行对比,如果支持,就说明这个监听器需要被回调。
2. 判断监听器会否支持类型,并且监听器是否支持sourceType,也就是springApplication,默认支持。

public boolean supportsEventType(ResolvableType eventType) {if (this.delegate instanceof SmartApplicationListener) {Class<? extends ApplicationEvent> eventClass = (Class<? extends ApplicationEvent>) eventType.resolve();return (eventClass != null && ((SmartApplicationListener) this.delegate).supportsEventType(eventClass));}else {return (this.declaredEventType == null || this.declaredEventType.isAssignableFrom(eventType));}
}
  1. 获取到事件所对应的监听器已结束,回调监听器。

相关文章:

SpringBoot 源码解析4:事件监听器

SpringBoot 源码解析4&#xff1a;事件监听器 1. 初始化监听器2. 创建事件发布器 SpringApplicationRunListeners3. 事件分发流程3.1 SimpleApplicationEventMulticaster#multicastEvent3.2 获取监听器 AbstractApplicationEventMulticaster#getApplicationListeners3.3 Abstra…...

使用 FastAPI 和 Vue.js 实现前后端分离

简介 前后端分离是现代 Web 开发的趋势。使用 FastAPI 和 Vue.js 可以构建一个高效、灵活且易于维护的 Web 应用。FastAPI 提供了高性能的后端服务&#xff0c;而 Vue.js 作为一种渐进式 JavaScript 框架&#xff0c;可以构建动态的前端界面。本文将详细介绍如何使用 FastAPI …...

算法基础之SPFA判断负环

SPFA判断负环 核心思想&#xff1a;spfa算法 当遍历一个点时 cnt数组记录边数 若有负环 边数会无限1 cnt>n是即为有负环 #include<iostream>#include<cstring>#include<algorithm>#include<queue>using namespace std;const int N 2010 , M 10010…...

一些常用的Linux命令及其简要说明(持续更新)

1. cd&#xff1a;改变当前工作目录。 cd [directory]#例如 cd /home/user 2. ls&#xff1a;列出目录内容。 ls [-options] [file/directory]#例如 ls -l, ls /etc 3. pwd&#xff1a;显示当前工作目录。 pwd 4. mkdir&#xff1a;创建新目录。 mkdir [directory]#例…...

开发企业展示小程序的关键步骤和技巧

随着移动互联网的快速发展&#xff0c;小程序已经成为企业展示形象、推广产品和服务的重要工具。拥有一个优秀的小程序可以帮助企业提高品牌知名度&#xff0c;吸引更多潜在客户&#xff0c;提升用户体验。以下是拥有一个展示小程序的步骤&#xff1a; 确定需求和目标 首先&am…...

Python-Selenium-使用 pywinauto 实现 Input 上传文件

当前环境&#xff1a;Win10 Python3.7 pywinauto0.6.8&#xff0c;selenium3.14.1 示例代码 from pywinauto import Desktop import osapp Desktop() dialog app[打开] dialog[Edit].set_edit_text(os.getcwd() .\\example-01.jpg) dialog[Button].click() 其他方法&…...

Go语言运行时与自家平台对比后认识

引子 以前就了解Go语言&#xff0c;因为其天生为并发、并行而生&#xff0c;且在语言层面就进行了内秉设计。 总想对比于我们自研的分布式并发、并行平台&#xff0c;以利于得到一些新认识 &#xff1a;&#xff09; Go官网资料 在Go的官网资料提供了很好的资料和知识库 初…...

leetcode 450. 删除二叉搜索树中的节点

leetcode 450. 删除二叉搜索树中的节点 题目 给定一个二叉搜索树的根节点 root 和一个值 key&#xff0c;删除二叉搜索树中的 key 对应的节点&#xff0c;并保证二叉搜索树的性质不变。返回二叉搜索树&#xff08;有可能被更新&#xff09;的根节点的引用。 一般来说&#x…...

小红书可观测 Metrics 架构演进,如何实现数十倍性能提升?

在当前云原生时代&#xff0c;随着微服务架构的广泛应用&#xff0c;云原生可观测性概念被广泛讨论。可观测技术建设&#xff0c;将有助于跟踪、了解和诊断生产环境问题&#xff0c;辅助开发和运维人员快速发现、定位和解决问题&#xff0c;支撑风险追溯、经验沉淀、故障预警&a…...

selenium学习

前期准备 pip install selenium 获取浏览器驱动 我使用的浏览器是Chrome&#xff0c;所以这里只介绍关于Chrome获取浏览器驱动的方法&#xff1a; 需要注意的是&#xff1a;selenium 4.x 对之前版本的部分API调用方式进行了调整&#xff0c;这里就包括关于浏览器获取驱动的方式…...

前端开发新趋势:Web3、区块链和虚拟现实

目录 前言 Web3&#xff1a;下一代互联网 区块链技术 去中心化应用程序&#xff08;DApps&#xff09; 区块链&#xff1a;重塑数字世界 数字钱包 NFT&#xff08;非同质化代币&#xff09; 虚拟现实&#xff1a;沉浸式体验 WebVR和WebXR 三维图形 新挑战与机会 性…...

如何安装运行Wagtail并结合cpolar内网穿透实现公网访问网站界面

文章目录 前言1. 安装并运行Wagtail1.1 创建并激活虚拟环境 2. 安装cpolar内网穿透工具3. 实现Wagtail公网访问4. 固定的Wagtail公网地址 前言 Wagtail是一个用Python编写的开源CMS&#xff0c;建立在Django Web框架上。Wagtail 是一个基于 Django 的开源内容管理系统&#xf…...

【>D:\10\Debug\RCa00828(34): fatal error RC1022: expected ‘#endif‘】

1>D:\10\Debug\RCa00828(34): fatal error RC1022: expected ‘#endif’ The error message you’re seeing, fatal error RC1022: expected ‘#endif’, indicates that the resource compiler encountered an issue when processing a resource script file (typically w…...

使用vite搭建项目时,在启动vite后,浏览器显示页面:找不到localhost的网页

现象 在使用前端工具vite&#xff08;版本5&#xff09;&#xff0c;搭建vue3项目时&#xff0c;启动vite&#xff0c;浏览器显示页面&#xff1a;找不到localhost的网页, 起初怀疑是 未加参数 --host0.0.0.0,导致&#xff0c;后加上该参数后问题依旧 解决 将index.html页面…...

libp2p 快速开始

文章目录 第一部分&#xff1a;libp2p 快速入门一、什么是libp2plibp2p 发展历程libp2p的特性p2p 网络和我们熟悉的 client/server 网络的区别&#xff1a; 二、Libp2p的实现目标三、Libp2p的用途四、运行 Libp2p 协议流程libp2p 分为三层libp2p 还有一个局域网节点发现协议 mD…...

【数据结构】——排序算法简答题模板

目录 一、内排序和外排序二、排序算法的稳定性三、插入排序&#xff08;一&#xff09;直接插入排序的步骤&#xff08;二&#xff09;直接插入排序的稳定性&#xff08;三&#xff09;折半插入排序的步骤&#xff08;四&#xff09;希尔排序的步骤 四、交换排序&#xff08;一…...

vue3.0基础

1. setup函数 vue单页面使用到的变量和方法都定义在setup函数中,return后才能被页面引用 export default {setup(){const name 张三const person {name,age:30}function goWork(){consle.log(工作)}return {name,person,goWork}} } 注意&#xff1a;直接定义的变量修改不会…...

Kafka本地安装⭐️(Windows)并测试生产消息以及消费消息的可用性

2023.12.17 天气晴 温度较低 十点半&#xff0c;不是不想起实在是阳光浴太nice了日常三连&#xff0c;喂&#xff0c;刷&#xff0c;肝刷会儿博客&#xff0c;看会儿设计模式冷冷冷 进被窝 刷视频 睡觉看看kafka的本地部署 》》实践》》成功写会儿博客&#xff0c…...

生产环境_Spark解析JSON字符串并插入到MySQL数据库

业务背景&#xff1a; 最近开发有一个需求&#xff0c;是这样的 我需要将一段从前端传过来的JSON字符串进行解析&#xff0c;并从中提取出所需的数据&#xff0c;然后将这些数据插入到MySQL数据库中。 json格式样例如下 { \"区域编号\": \"001\", …...

WEB渗透—PHP反序列化(四)

Web渗透—PHP反序列化 课程学习分享&#xff08;课程非本人制作&#xff0c;仅提供学习分享&#xff09; 靶场下载地址&#xff1a;GitHub - mcc0624/php_ser_Class: php反序列化靶场课程&#xff0c;基于课程制作的靶场 课程地址&#xff1a;PHP反序列化漏洞学习_哔哩…...

LVS-DR模式部署

实验准备&#xff1a; 节点服务器 192.168.116.20 #web1 192.168.116.30 #web2 1.部署NFS共享存储 2.部署Web节点服务器 将两台服务器的网关注释掉 #重启网卡 systemctl restart network 修改节点服务器的内核参数|vim /etc/sysctl.conf net.ipv4.conf.lo.arp_ign…...

Oracle的学习心得和知识总结(三十)| OLTP 应用程序的合成工作负载生成器Lauca论文翻译及学习

目录结构 注&#xff1a;提前言明 本文借鉴了以下博主、书籍或网站的内容&#xff0c;其列表如下&#xff1a; 1、参考书籍&#xff1a;《Oracle Database SQL Language Reference》 2、参考书籍&#xff1a;《PostgreSQL中文手册》 3、EDB Postgres Advanced Server User Gui…...

HarmonyOS4.0从零开始的开发教程18后台代理提醒

HarmonyOS&#xff08;十六&#xff09;后台代理提醒 简介 随着生活节奏的加快&#xff0c;我们有时会忘记一些重要的事情或日子&#xff0c;所以提醒功能必不可少。应用可能需要在指定的时刻&#xff0c;向用户发送一些业务提醒通知。例如购物类应用&#xff0c;希望在指定时…...

智能优化算法应用:基于算术优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于算术优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于算术优化算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.算术优化算法4.实验参数设定5.算法结果6.…...

在vue中通过js动态绘制table,并且合并连续相同内容的行,支持点击编辑单元格内容

首先是vue代码 <template><div id"body-container"style"position: absolute"><div class"box-container"><div class"lsb-table-box" ><div class"table-container" id"lsb-table"&…...

输电线路定位:精确导航,确保电力传输安全

在现代社会中&#xff0c;电力作为生活的基石&#xff0c;其安全稳定运行至关重要。而输电线路作为电力传输的重要通道&#xff0c;其故障定位和修复显得尤为重要。恒峰智慧科技将为您介绍一种采用分布式行波测量技术的输电线路定位方法&#xff0c;以提高故障定位精度&#xf…...

ZKP Commitment (1)

MIT IAP 2023 Modern Zero Knowledge Cryptography课程笔记 Lecture 5: Commitment 1 (Ying Tong Lai) Overview: Modern SNARK IOP: Interactive Oracle ProofCommitment SchemeIOP “compiled by” the commitment scheme to get a non-interactive proofAn IOP is “inform…...

【难点】【LRU】146.LRU缓存

题目 法1&#xff1a;基于Java的LinkedHashMap 必须掌握法1。参考链接 关于LinkedHashMap的介绍 class LRUCache {int cap;LinkedHashMap<Integer, Integer> cache new LinkedHashMap<>();public LRUCache(int capacity) { this.cap capacity;}public int get…...

基于YOLOv8深度学习的吸烟/抽烟行为检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测、深度学习实战

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…...

菜鸟学习日记(python)——匿名函数

Python 使用 lambda 来创建匿名函数。 lambda 函数是一种小型、匿名的内联函数&#xff0c;它可以具有任意数量的参数&#xff0c;但只能有一个表达式。 匿名函数的一般格式如下&#xff1a; lambda 参数列表:表达式 表达式用于计算并返回函数结果 lambda 函数通常用于编写…...