Spring 中的Event机制

news/2025/11/4 22:26:22/文章来源:https://www.cnblogs.com/dddy/p/19191523

spring

参考资料:
Additional Capabilities of the ApplicationContext- https://docs.spring.io/spring-framework/reference/6.1/core/beans/context-introduction.html

[[17.行为型 - 观察者模式 (Observer Pattern)]]
[[Spring IOC 源码学习总笔记]]

Spring Event

Standard and Custom Events

Event handling in the ApplicationContext is provided through the ApplicationEvent class and the ApplicationListener interface. If a bean that implements the ApplicationListener interface is deployed into the context, every time an ApplicationEvent gets published to the ApplicationContext, that bean is notified. Essentially, this is the standard Observer design pattern.

简而言之 两点:

  1. 推送到 ApplicationContext 中的事件, 都通知容器中实现 ApplicationEvent 接口的 bean.
  2. this is the standard Observer design pattern 它是标准的观察者模式

观察者模式和发布-订阅模式

观察者模式的别名有发布-订阅(Publish/Subscribe)模式, 我们来看一下观察者模式与发布订阅模式结构上的区别

  • 在设计模式结构上,发布订阅模式继承自观察者模式,是观察者模式的一种 实现的变体。
  • 在设计模式意图上,两者关注点不同,一个关心数据源,一个关心的是事件消息。

截图_2025-11-04_22-24-18

对比标准的观察者模式:

  • 观察者模式:数据源直接通知订阅者发生改变
  • 发布订阅模式:数据源(被观察者) 告诉第三方(Event Channel) 发生了改变,第三方(Event Channel)再通知/广播订阅者(观察者) 发生了改变

Spring 其实基于是发布订阅模式, 主要由ApplicationEventMulticaster 管理订阅者/事件通道/广播事件;

事件模型的关键角色对象

1.ApplicationEvent 事件源(中间传递参数)

事件源对象, 在事件中传递的中间参数

org.springframework.context.ApplicationEvent

public abstract class ApplicationEvent extends EventObject {// 它继承了 java.util.EventObject/***定义在 java.util.EventObject 中, 可以通过该方法获取到事件源对象public Object getSource() {  return this.source;}***//**  * Create a new {@code ApplicationEvent} with its {@link #getTimestamp() timestamp}  * set to {@link System#currentTimeMillis()}.  * @param source the object on which the event initially occurred or with  * which the event is associated (never {@code null})  * @see #ApplicationEvent(Object, Clock)  */  public ApplicationEvent(Object source) {  super(source);  this.timestamp = System.currentTimeMillis();  }/**  * Return the time in milliseconds when the event occurred.  * 返回事件发生时间* @see #ApplicationEvent(Object)  * @see #ApplicationEvent(Object, Clock)  */  public final long getTimestamp() {  return this.timestamp;  }}

2.ApplicationListener 监听器

充当 '观察者' 角色

org.springframework.context.ApplicationListener

@FunctionalInterface  
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {  /**  * Handle an application event.  * 处理事件的方法.* @param event the event to respond to  */  void onApplicationEvent(E event);...
}

3.ApplicationEventPublisher 事件推送器

充当 '被观察者' 角色

@FunctionalInterface  
public interface ApplicationEventPublisher {/**  * Notify all <strong>matching</strong> listeners registered with this  * application of an event.  * <p>If the specified {@code event} is not an {@link ApplicationEvent},  * it is wrapped in a {@link PayloadApplicationEvent}.  * <p>Such an event publication step is effectively a hand-off to the  * multicaster and does not imply synchronous/asynchronous execution  * or even immediate execution at all. Event listeners are encouraged  * to be as efficient as possible, individually using asynchronous  * execution for longer-running and potentially blocking operations.  * @param event the event to publish  * @since 4.2  * @see #publishEvent(ApplicationEvent)  * @see PayloadApplicationEvent  */  void publishEvent(Object event);  }

4.ApplicationEventMulticaster 事件广播器

**充当 第三方(Event Channel) 角色, 它通知/广播 消息给观察者

管理/广播/群发事件, 注册事件监听器, 管理事件通道, spring 其实最终是委托给该对象广播事件的

org.springframework.context.event.ApplicationEventMulticaster

/**
* Interface to be implemented by objects that can manage a number of
**/
public interface ApplicationEventMulticaster {  /**  
* Add a listener to be notified of all events.  
* @param listener the listener to add  
* @see #removeApplicationListener(ApplicationListener)  
* @see #removeApplicationListeners(Predicate)  
*/  
void addApplicationListener(ApplicationListener<?> listener);  /**  
* Add a listener bean to be notified of all events.  
* @param listenerBeanName the name of the listener bean to add  
* @see #removeApplicationListenerBean(String)  
* @see #removeApplicationListenerBeans(Predicate)  
*/  
void addApplicationListenerBean(String listenerBeanName);  /**  
* Remove a listener from the notification list.  
* @param listener the listener to remove  
* @see #addApplicationListener(ApplicationListener)  
* @see #removeApplicationListeners(Predicate)  
*/  
void removeApplicationListener(ApplicationListener<?> listener);  /**  
* Remove a listener bean from the notification list.  
* @param listenerBeanName the name of the listener bean to remove  
* @see #addApplicationListenerBean(String)  
* @see #removeApplicationListenerBeans(Predicate)  
*/  
void removeApplicationListenerBean(String listenerBeanName);  /**  
* Remove all matching listeners from the set of registered  
* {@code ApplicationListener} instances (which includes adapter classes  
* such as {@link ApplicationListenerMethodAdapter}, e.g. for annotated  
* {@link EventListener} methods).  
* <p>Note: This just applies to instance registrations, not to listeners  
* registered by bean name.  
* @param predicate the predicate to identify listener instances to remove,  
* e.g. checking {@link SmartApplicationListener#getListenerId()}  
* @since 5.3.5  
* @see #addApplicationListener(ApplicationListener)  
* @see #removeApplicationListener(ApplicationListener)  
*/  
void removeApplicationListeners(Predicate<ApplicationListener<?>> predicate);  /**  
* Remove all matching listener beans from the set of registered  
* listener bean names (referring to bean classes which in turn  
* implement the {@link ApplicationListener} interface directly).  
* <p>Note: This just applies to bean name registrations, not to  
* programmatically registered {@code ApplicationListener} instances.  
* @param predicate the predicate to identify listener bean names to remove  
* @since 5.3.5  
* @see #addApplicationListenerBean(String)  
* @see #removeApplicationListenerBean(String)  
*/  
void removeApplicationListenerBeans(Predicate<String> predicate);  /**  
* Remove all listeners registered with this multicaster.  
* <p>After a remove call, the multicaster will perform no action  
* on event notification until new listeners are registered.  
* @see #removeApplicationListeners(Predicate)  
*/  
void removeAllListeners();  /**  
* Multicast the given application event to appropriate listeners.  
* <p>Consider using {@link #multicastEvent(ApplicationEvent, ResolvableType)}  
* if possible as it provides better support for generics-based events.  
* @param event the event to multicast  
*/  
void multicastEvent(ApplicationEvent event);  /**  
* Multicast the given application event to appropriate listeners.  
* <p>If the {@code eventType} is {@code null}, a default type is built  
* based on the {@code event} instance.  
* @param event the event to multicast  
* @param eventType the type of event (can be {@code null})  
* @since 4.2  
*/  
void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);  }

内置的事件类型

官方文档 - Standard and Custom Events

public class BusinessService implements ApplicationListener {@Override  public void onApplicationEvent(ApplicationEvent event) {  if(event instanceof ContextRefreshedEvent) {  //容器启动完成事件  }else if(event instanceof ContextClosedEvent) {  //容器关闭事件excutor.shutdownNow();  log.info("业务线程池终止");  }}
}
  • ContextRefreshedEvent ApplicationContext容器初始化或刷新触发该事件。此处说的初始化,是指所有的bean被成功加载,后处理的bean被检测激活,所有的singleton bean被预初始化,ApplicationContext容器已就绪可用。容器完整可用

  • ContextStartdEvent: 当使用ApplicationContext的子接口ConfigurableApplicationContex接口的start()方法启动ApplicationContext容器时触发该事件。容器管理生命周期的bean实例将获得一个指定的启动信号,这在经常需要停止后重新启动的场合比较常见。

  • ContextClossedEvent:当使用ConfigurableApplicationContex接口的close()方法关闭ApplicationContext容器时触发该事件。

  • ContextStoppedEvent:当使用ConfigurableApplicationContex接口的stop()方法使ApplicationContext容器停止时触发该事件 。此处的“停止”意味着,容器管理生命周期的bean实例将获得一个指定的停止信号。被停止的spring容器可以再次通过调用start()方法重新启动。

  • RequestHandledEventWeb:相关的事件,只能应用于使用DispatcherServlet的Web应用中。在使用spring作为前端的MVC控制器时,当spring处理用户请求结束后,系统会自动触发该事件。

自定义事件实例

EventObject 事件定义

定义事件,继承 ApplicationEvent 的类成为一个事件类

@Data  
@ToString  
public class OrderProductEvent extends ApplicationEvent {    /** 该类型事件携带的信息 */  
  private String orderId;    public OrderProductEvent(Object source, String orderId) {  
    super(source);  
    this.orderId = orderId;  
  }  
}

Listener 监听定义

监听并处理事件,实现 ApplicationListener 接口或者使用 @EventListener 注解

@Slf4j  
@Component  
public class OrderProductListener implements ApplicationListener<OrderProductEvent> {    /** 使用 onApplicationEvent 方法对消息进行接收处理 */  
  @SneakyThrows  
  @Override  
  public void onApplicationEvent(OrderProductEvent event) {  
    String orderId = event.getOrderId();  
    long start = System.currentTimeMillis();  
    Thread.sleep(2000);  
    long end = System.currentTimeMillis();  
    log.info("{}:校验订单商品价格耗时:({})毫秒", orderId, (end - start));  
  }  
}

在 监听器(Listener) onApplicationEvent 中的线程与 发布(push)线程 不是同一个线程, 它们的 ThreadLocal上下文是​​完全隔离的​​。不​继承原有事务。

Push 推送定义

如何自定义push事件?
使用 ApplicationEventPublisherApplicationContext 都可

//@Autowired
//ApplicationEventPublisher publisher;@Autowired
ApplicationContext applicationContext;public void publish(String msg){applicationContext.publishEvent(new OrderProductEvent());
}

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

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

相关文章

jiangly模板-字符串

目录 目录马拉车(Manacher)Z 函数后缀数组后缀自动机(SAM 新版)KMP马拉车(Manacher) /** 马拉车(Manacher, with string)* 2024-04-06: https://qoj.ac/submission/380047* 2024-04-09: https://qoj.a…

Java 内存模型(JMM)中 volatile 的作用与限制

volatile关键字的主要作用是限制JVM进行指令重排,保证变量对其他线程的可见性,可以避免创建对象才完成了一半就被其他线程引用到这个对象,保证创建完成后才对其他线程可见。static class Value {int x; // 默认 0Va…

今日学习:二分

P3853 天津省选 /* 这个题和P2678几乎一样,但说实话我还没看懂。 1.首先检查的标准我没想到。是要检查当前的空旷指数还是路标数? 2.其次check的逻辑我还是没想明白。 3.犯了个小错。计算mid应该放在while循环里面。…

Ice Breaker Games - 一个在线免费的游戏网站,无需登录,打开即玩。

Ice Breaker Games - 一个在线免费的游戏网站,无需登录,打开即玩。 https://www.icebreakgame.com/

Java获取当前时间的下一天以及30天前的时间

有这样的一个需求。需要得到当前时间的下一天以及30前的时间。在计算30天前的时间时出现了一点问题,时间出错,但是公式没有出错,后来才发现是运算超出了Integer的范围。( 24 * 60 * 60*1000) 这种计算表达式在 Java…

论文导读:从 TSMC ISSCC 看 SRAM 存算发展

上次集中学习存算工作还是一年半以前,时光如梭,SRAM CIM 范式对比记忆又有新花样。本篇 blog 针对 ISSCC 2024 34.4 TSMC 的 3 nm 数字 SRAM 近存算 Macro 个例分析,并结合架构视角谈谈个人感触[1]。 RAM 的物理-逻…

edge chromium浏览器copilot图标消失处理

解决edge浏览器copilot图标消失找到edge浏览器的配置文件Local State %APPDATA%\..\Local\Microsoft\Edge\User Data修改配置variations_country 改成US,记得任务管理器完全关闭edge浏览器后修改

AI - 自然语言处理(NLP) - part 2 - 词向量 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

洛谷 P4577

题面太屎了。给定一棵大小为 \(n\) 的树,每个节点有权值 \(a_i\),问最多能选出多少个节点,使得若 \(v \in subtree_u\),则 \(a_v \ge a_u\) 成立。 \(n \le 2 \times 10^5\)。这个问题丢到序列上就是 \(LIS\) 了,…

C++算法贪心例题讲解 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

AI元人文:理论框架、僵局本质与文明演化的系统性构想

AI元人文:理论框架、僵局本质与文明演化的系统性构想 一、核心命题与理论基石核心命题:从“价值对齐”到“价值权衡”的范式革命传统AI的“价值对齐”范式陷入三重困境:认知科学误区(沿用过时的信息处理模型)、方…

[linux-mint] Surface Pro4 安装linux驱动

Surface Pro4 这个被淘汰下来的机器,一直没想好要如何处理,因为有平板,所以也不想刷Android系统,最终决定还是安装linux,当一个办公小平板来使用,顺便练练linux环境软件使用,所以就选择系统,然后发现好多linux…

[B] AGC VP 记录

AtCoder Grand Contest 049 AT_agc049_e [AGC049E] Increment Decrement AtCoder Grand Contest 052 约 1h 切 A,之后都不会了。 A - Long Common Subsequence 先从左到右放 \(n\) 个 \(0\),\(n\) 个 \(1\)。发现 \(…

2025年河南工业大学2025新生周赛(2)

A 小唐的签到 小唐到达教室的时间等于路上所用时间和上楼时间之和,注意如果教室在n楼,只需要上n-1层。#include<bits/stdc++.h> using namespace std; int main() {int a, x, n, b, y;cin >> a >> …

Atcoder [ARC161C] Dyed by Majority (Odd Tree) 题解 [ 绿 ] [ 树的遍历 ] [ 构造 ] [ 贪心 ]

Dyed by Majority (Odd Tree) 想起来无聊,写起来恶心。 首先手模一下,发现叶子节点可以确定它父亲的颜色。这启示我们自底向上确定颜色。 因此考虑在已确定所有儿子的颜色时,确定自己的颜色,此时有两种情况:儿子中…

Reflections on Trusting Trust by Ken Thompson

来源:https://aeb.win.tue.nl/linux/hh/thompson/trust.htmlKen Thompsons "cc hack" - Presented in the journal, Communication of the ACM, Vol. 27, No. 8, August 1984, in a paper entitled "R…

[Agent] ACE(Agentic Context Engineering)源码阅读笔记---(1)基础模块

[Agent] ACE(Agentic Context Engineering)源码阅读笔记---(1)基础模块 目录[Agent] ACE(Agentic Context Engineering)源码阅读笔记---(1)基础模块0x00 概要0x01 示例1.1 建立简单Agent1.2 后续操作Load and …

AI大语言模型从0开发

Transformer Tokenization 考虑到计算机没有办法直接识别人类语言,我们将每一个词映射为一个token使得计算机可以直接识别。 为实现这个目的我们使用BPE算法将每个词划分为若干个前缀和后缀,以此拼起来一个词,节省v…

第三十三篇

今天是11月4号,钳工实训真难。累死了。