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

Spring-事件

Java 事件/监听器编程模型

设计模式-观察者模式的拓展

  • 可观察者对象(消息发送者) Java.util.Observalbe
  • 观察者 java.util.Observer

标准化接口(标记接口)

  • 事件对象 java.util.EventObject
  • 事件监听器 java.util.EventListener
public class ObserverDemo {public static void main(String[] args) {Observable observable = new EventObservable();observable.addObserver(new EventObserver());observable.notifyObservers("hello");}/*** 因为我们要调用 change 监听者这个方法才能生效 但是这个方法是个protected 所以我们进行拓展* */static class EventObservable extends Observable {public void setChanged(){super.setChanged();}public void notifyObservers(Object args) {setChanged();super.notifyObservers(new EventObject(args));clearChanged();}}static class EventObserver implements Observer, EventListener {@Overridepublic void update(Observable o, Object event) {EventObject eventObject = (EventObject) event;System.out.println("收到消息:" + eventObject);}}
}

理解:
发布事件的是被监听的对向,里面会注册监听器,也就是需要感知当前对象变化的对象。
JDKEventListener提供了这个标记接口,算是一种规范,表名这个是事件的监听器。
EventObject 这个也算是一个标准,这个对象是方便数据在事件发布的时候进行传递。

面向接口的事件/监听器设计模式

在这里插入图片描述
基本模式:
一般监听器会继承EventListener
一般事件会继承EventObject

面向注解的事件/监听器设计模式

在这里插入图片描述

Spirng 标准事件 ApplicationEvent

在这里插入图片描述

基于接口的事件监听器

在这里插入图片描述

public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();// 注册一个事件监听context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println(event.getTimestamp() + "接收到事件 : " + event);}});context.refresh();context.close();
}

可以看到这里收到了两个事件,那么事件从哪里发布的呢?请看事件发布器。
在这里插入图片描述

基于注解的事件监听器

在这里插入图片描述

public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(ApplicatonListenrDemo.class);context.refresh();context.close();
}
@EventListener
public void onApplicationEvent(ApplicationEvent applicationEvent) {System.out.println(applicationEvent);
}
// 这样的话就会分类别来处理
@EventListener
public void onApplicationEvent(ContextRefreshedEvent applicationEvent) {System.out.println(applicationEvent + "re");
}
// 异步处理
@EnableAsync
public class ApplicatonListenrDemo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(ApplicatonListenrDemo.class);context.refresh();context.close();}@EventListenerpublic void onApplicationEvent(ApplicationEvent applicationEvent) {System.out.println(applicationEvent);}@EventListenerpublic void onApplicationEvent(ContextRefreshedEvent applicationEvent) {System.out.println(applicationEvent + "re");}@EventListener@Asyncpublic void onApplicationEvent(ContextClosedEvent applicationEvent) {System.out.println(applicationEvent);System.out.println(Thread.currentThread().getId());}
}
// 控制顺序
public class ApplicatonListenrDemo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(ApplicatonListenrDemo.class);context.refresh();context.close();}@EventListener@Order(2)public void onApplicationEvent2(ContextRefreshedEvent applicationEvent) {System.out.println("=======");}@EventListener@Order(1)public void onApplicationEvent1(ContextRefreshedEvent applicationEvent) {System.out.println("****");}
}

注册Spirng ApplicationListenner

方法一: ApplicationListener 作为SpirngBean注册

context.register(MyEventListener.class);
static class MyEventListener implements ApplicationListener<ApplicationEvent> {@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println("=====>");}
}

方法二:通过ConfigrableApplicationContextAPI 注册

context.addApplicationListener(new ApplicationListener<ApplicationEvent>() {@Overridepublic void onApplicationEvent(ApplicationEvent event) {System.out.println(event.getTimestamp() + "接收到事件 : " + event);}
});

事件发布器

在这里插入图片描述

public class ApplicatonListenrDemo implements ApplicationEventPublisherAware {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(ApplicatonListenrDemo.class);context.refresh();context.start();context.close();}@EventListenerpublic void onApplicationEvent(ApplicationEvent event) {System.out.println(event);}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {applicationEventPublisher.publishEvent(new ApplicationEvent("hello") {});// 发布任意对象 重载方法applicationEventPublisher.publishEvent("yes");}
}

事件发布器如何找到对应的监听器进行实事件的发布呢? 在发布时间的时候会查缓存,缓存如果没有对应的监听器,则会更具事件泛型类型进行判断。
在这里插入图片描述
根据事件的泛型类型进行判断,如果类型符合加入监听器数组。
在这里插入图片描述

Spring 事件传播

在这里插入图片描述

public static void main(String[] args) {// 1 创建 parent Spring 应用上下文AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();parent.setId("parent");parent.register(Mylistener.class);// 创建current spring 应用上下文AnnotationConfigApplicationContext current = new AnnotationConfigApplicationContext();current.setId("current");current.register(Mylistener.class);current.setParent(parent);// current parentparent.refresh();current.refresh();parent.close();current.close();}
static class Mylistener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {System.out.printf("监听到应用上下文[ID %s]\n", event.getApplicationContext().getId());}
}

第一个事件触发是parent,第二,三个由于事件传播子和父都触发了这个事件:
在这里插入图片描述

原理就是,源码会在父也发布事件:
在这里插入图片描述
如何避免:

public class HierachicalEventDemo {public static void main(String[] args) {// 1 创建 parent Spring 应用上下文AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext();parent.setId("parent");parent.register(Mylistener.class);// 创建current spring 应用上下文AnnotationConfigApplicationContext current = new AnnotationConfigApplicationContext();current.setId("current");current.register(Mylistener.class);current.setParent(parent);// current parentparent.refresh();current.refresh();parent.close();current.close();}static class Mylistener implements ApplicationListener<ContextRefreshedEvent> {/*** 这里之所以要静态是因为我们在 parent 和 current 是不是一样的对象 也就是有两对象* 但是静态字段就是类共用的* 如果时间发布过不再重新发布*/private static Set<ApplicationEvent> processedEvents = new LinkedHashSet();@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {if (processedEvents.add(event)) {System.out.printf("监听到应用上下文[ID %s] %s\n ", event.getApplicationContext().getId(), event);}}}
}

Spirng 内建事件

在这里插入图片描述

Spring Payload 事件

在这里插入图片描述使用的时候不能简单继承使用,发送方法最好是用object这个方法。

自定义Spirng事件

在这里插入图片描述

public class MyEvent extends ApplicationEvent {public MyEvent(String msg) {super(msg);}@Overridepublic String getSource() {return (String) super.getSource();}public String getMessage() {return getSource();}
}
public class MyListener implements ApplicationListener<MyEvent> {@Overridepublic void onApplicationEvent(MyEvent event) {System.out.println(event.getMessage());}
}
public class Demo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(Mylistener.class);context.refresh();context.publishEvent(new MyEvent("test event"));context.close();}
}

事件发布注入

ApplicationEventPublisherAwae 回调接口
通过@Autowired ApplicationEventPublisher

依赖查找

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

ApplicationEventMulticaster的底层实现

在这里插入图片描述
AbstractContext事件分发布是通过ApplicationEventMulticaster来实现的:
在这里插入图片描述

同步和异步Spirng事件广播

在这里插入图片描述
如果是异步如果要设置Executeor 是需要类型转换的,不是基于接口的编程方式。

public class Demo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(Mylistener.class);context.refresh();ApplicationEventMulticaster multicaster = context.getBean(ApplicationEventMulticaster.class);if (multicaster instanceof SimpleApplicationEventMulticaster) {ExecutorService executor = newSingleThreadExecutor();SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = (SimpleApplicationEventMulticaster) multicaster;simpleApplicationEventMulticaster.setTaskExecutor(executor);// 优雅的关闭线程池simpleApplicationEventMulticaster.addApplicationListener(new ApplicationListener<ContextClosedEvent>() {@Overridepublic void onApplicationEvent(ContextClosedEvent event) {if (!executor.isShutdown()) {executor.shutdown();}}});}context.publishEvent(new MyEvent("test event"));context.close();}
}

通过注解的方式实现:

@EnableAsync
public class Demo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(Demo.class);context.refresh();context.publishEvent(new MyEvent("test event"));context.close();}@EventListener@Asyncpublic void onApplicationContext(ApplicationEvent event) {System.out.println(Thread.currentThread().getName() +  event);}// 这是自定义我们的线程池@BeanExecutor taskExecutor() {return Executors.newSingleThreadExecutor();}
}

事件的异常情况

在这里插入图片描述

public class Demo {public static void main(String[] args) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();context.register(Demo.class);context.refresh();ApplicationEventMulticaster multicaster = context.getBean(ApplicationEventMulticaster.class);if (multicaster instanceof SimpleApplicationEventMulticaster) {SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = (SimpleApplicationEventMulticaster) multicaster;simpleApplicationEventMulticaster.setErrorHandler((t) ->{System.out.printf("发生了异常" );});}context.publishEvent(new MyEvent("test event"));context.close();}@EventListenerpublic void onApplicationContext(ContextClosedEvent event) {System.out.println(Thread.currentThread().getName() +  event);throw new RuntimeException("制造异常");}@BeanExecutor taskExecutor() {return Executors.newSingleThreadExecutor();}
}

Spirng 事件/监听实现原理

在这里插入图片描述
在这里插入图片描述
ListenerRetriever 会过滤对应的ApplicationListener Event实例 这个Event实例包括它本身及它的孙子类
在这里插入图片描述
处理泛型并过滤:
在这里插入图片描述
在处理事件的时候就会去获取对应的监听器:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
参考资料:小马哥核心编程思想。

相关文章:

Spring-事件

Java 事件/监听器编程模型 设计模式-观察者模式的拓展 可观察者对象(消息发送者) Java.util.Observalbe观察者 java.util.Observer 标准化接口(标记接口) 事件对象 java.util.EventObject事件监听器 java.util.EventListener public class ObserverDemo {public static vo…...

delmia的工序设置

process的设置需要在workcell sequuencing里面去设置...

【JavaEE精炼宝库】多线程(5)单例模式 | 指令重排序 | 阻塞队列

目录 一、单例模式&#xff1a; 1.1 饿汉模式&#xff1a; 1.2 懒汉模式&#xff1a; 1.2.1 线程安全的懒汉模式&#xff1a; 1.2.2 线程安全的懒汉模式的优化&#xff1a; 二、指令重排序 三、阻塞队列 3.1 阻塞队列的概念&#xff1a; 3.2 生产者消费者模型&#xf…...

[图解]《分析模式》漫谈03-Party是什么

1 00:00:00,790 --> 00:00:03,930 今天我们来看一下&#xff0c;Party是什么 2 00:00:05,710 --> 00:00:07,470 当然我们这里说的不是政治的 3 00:00:07,880 --> 00:00:08,350 Party 4 00:00:09,230 --> 00:00:11,110 是《分析模式》里面的一个用词 5 00:00:14…...

【Numpy】一文向您详细介绍 np.abs()

【Numpy】一文向您详细介绍 np.abs() 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&#xff0c;曾…...

【AI绘画】Stable Diffusion 3开源

Open Release of Stable Diffusion 3 Medium 主要内容 Stable Diffusion 3是Stability AI目前为止最先进的文本转图像开放源代码算法。 这款模型的小巧设计使其完美适合用于消费级PC和笔记本电脑&#xff0c;以及企业级图形处理单元上运行。它已经满足了标准化的文字转图像模…...

使用ant-design/cssinjs向plasmo浏览器插件的内容脚本content中注入antd的ui组件样式

之前写过一篇文章用来向content内容脚本注入antd的ui&#xff1a;https://xiaoshen.blog.csdn.net/article/details/136418199&#xff0c;但是方法就是比较繁琐&#xff0c;需要将antd的样式拷贝出来&#xff0c;然后贴到一个单独的css样式文件中&#xff0c;然后引入到内容脚…...

南京威雅学校:初中转轨国际化教育,她们打开了成长的另一种可能

“上了大学就轻松了。” 又是一年高考季&#xff0c;每每回想起十八岁前那些没日没夜埋头学习的日子&#xff0c;已经为人父母的你是不是也忍不住想要孩子气地吐槽一句&#xff0c;“骗人”——人不会在一场考试后瞬间长大&#xff0c;试卷里也没有人生的全部答案。 三年前&a…...

Linux | 标准IO编程

Linux | 标准IO编程 时间:2024年6月8日23:03:43 文章目录 `Linux` | 标准`IO`编程1.标准`IO`编程1-1.流的打开函数fopen()1-2.流的关闭函数fclose()1-3.错误处理函数perror()函数strerror()errno 变量总结1-4.流的读写1-4-1.按字符(字节)输入/输出实例1-4-2.按行输入/输出1-…...

从ES的JVM配置起步思考JVM常见参数优化

目录 一、真实查看参数 &#xff08;一&#xff09;-XX:PrintCommandLineFlags &#xff08;二&#xff09;-XX:PrintFlagsFinal 二、堆空间的配置 &#xff08;一&#xff09;默认配置 &#xff08;二&#xff09;配置Elasticsearch堆内存时&#xff0c;将初始大小设置为…...

milvus的GPU索引

前言 milvus支持多种GPU索引类型&#xff0c;它能加速查询的性能和效率&#xff0c;特别是在高吞吐量&#xff0c;低延迟和高召回率的场景。本文我们将介绍milvus支持的各种GPU索引类型以及它们适用的场景、性能特点。 下图展示了milvus的几种索引的查询性能对比&#xff0c;…...

CleanMyMac2024最新免费电脑Mac系统优化工具

大家好&#xff0c;我是你们的好朋友——软件评测专家&#xff0c;同时也是一名技术博主。今天我要给大家种草一个超级实用的Mac优化工具——CleanMyMac&#xff01; 作为一个长期使用macOS的用户&#xff0c;我深知系统运行时间长了&#xff0c;缓存文件、日志、临时文件等都会…...

catia/delmia的快捷图标模式最多12个

这儿最多显示12个 根据官方文档 If you installed a configuration containing more than 12 workbenches (such as the "AL2" configuration), only the first 12 workbenches are displayed in the Favorites list. The other workbenches do not appear in the l…...

磁盘性能概述与磁盘调度算法

目录 1. 磁盘性能概述 1. 数据传输速率 2. 寻道时间 3. 旋转延迟 4. 平均访问时间 2. 早期的磁盘调度算法 1. FIFO&#xff08;First-In-First-Out&#xff09;调度算法 2. SSTF&#xff08;Shortest Seek Time First&#xff09;调度算法 3. SCAN&#xff08;Elevator…...

chrome浏览器设置--disable-web-security解决跨域

在开发人员于后台进行接口测试的时候&#xff0c;老是遇到跨域问题&#xff0c;这时前端总是会让后台添加跨域请求头来允许跨域请求&#xff0c;今天介绍一个简单的方法跨过这一步操作的设置。 –disable-web-security参数&#xff0c;禁用同源策略&#xff0c;利于开发人员本…...

Android中蓝牙设备的状态值管理

在Android中&#xff0c;蓝牙状态可以通过多种方式来描述&#xff0c;主要包括蓝牙适配器状态、蓝牙设备连接状态以及蓝牙广播状态&#xff0c;其关键的蓝牙状态实现类有BluetoothAdapter、BluetoothDevicePairer、BluetoothDevice、BluetoothProfile&#xff0c;详细介绍如下&…...

关于ReactV18的页面跳转传参和接收

一、使用路由方式进行传参和接收&#xff08;此处需使用 useNavigate 和 useParams 两个hooks&#xff09; 1 首先需要配置好路由形式如下 :id(参数) { path: "/articleDetail/:id", element: lazyElement(<ArticleDetail />), }, 2 传递参数 使用 useNaviga…...

南京观海微电子-----PCB设计怎样降低EMI

开关模式电源是AC-DC或DC-DC电源的通用术语&#xff0c;这些电源使用具有快速开关动作的电路进行电压转换/转换(降压或升压)。随着每天开发出更多的设备(潜在的EMI受害者)&#xff0c;克服EMI成为工程师面临的主要挑战&#xff0c;并且实现电磁兼容性(EMC)与使设备正常运行同等…...

黑苹果/Mac如何升级 Mac 新系统 Sequoia Beta 版

Mac升级教程 有必要提醒一下大家&#xff0c;开发者测试版系统一般是给开发者测试用的&#xff0c;可能存在功能不完善、部分软件不兼容的情况&#xff0c;所以不建议普通用户升级&#xff0c;如果实在忍不住&#xff0c;升级之前记得做好备份。 升级方法很简单&#xff1a; …...

2024年主流工单系统横向对比

一&#xff1a;智齿科技 智齿客服App可以接收工单、查看工单、分配工单、处理工单&#xff0c;客户问题随时随地快速解决。 与云客户中心实时连接&#xff0c;客户以往的浏览轨迹、聊天信息、通话记录、工单历史一目了然。 配合智齿云呼叫中心/机器人客服/人工在线客服&…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验

一、多模态商品数据接口的技术架构 &#xff08;一&#xff09;多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如&#xff0c;当用户上传一张“蓝色连衣裙”的图片时&#xff0c;接口可自动提取图像中的颜色&#xff08;RGB值&…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统&#xff0c;支持多语言对话&#xff08;如 中文&#xff0c;英文&#xff0c;日语&#xff09;&#xff0c;语音情感&#xff08;如 开心&#xff0c;悲伤&#xff09;&#x…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...