一文讲懂Spring Event事件通知机制

news2024/9/21 0:48:29

目录

一 什么是spring event

二 怎么实现spring event


一 什么是spring event

        我不会按照官方的解释来说什么是spring event,我只是按照自己的理解来解释,可能原理上会和官方有偏差,但是它的作用和功能就是这个,我更加偏向于从他的功能方面去解释,官方的解释其实有点听不懂。

        ok,说了这么多,到底什么是spring event?我相信event大家都很熟悉,就是事件,那么我么在学前端的时候肯定都知道事件,最常用的就是点击事件,点击一个按钮就触发一个事件。我觉得这里也可以这么理解,只是区别在于这里没有按钮,所以我们只能监听,比如当某个条件达成后,就会自动触发事件。所以,我对于spring event理解就是这样。只是这个监听的对象,在接下来我将一步一步给大家讲清楚。

二 怎么实现spring event

        正如我上面所说的,spring event本身的功能不过就是监听我们设置的某个条件如果触发了,那么就会触发事件,然后执行我们想要执行的代码逻辑。那么很简单,我们只需要定义一个事件监听器,然后监听某个事件,当这个事件被触发,我们就可以执行监听器里面的代码逻辑。

        没错,代码也确实如此,spring提供了一个 ApplicationListener<xxx>接口。只要实现这个接口,就相当于一个事件监听器,尖括号里面的就是我们监听的事件对象,当这个监听的对象达到条件,这个接口里面有一个onApplicationEvent()方法就会被执行,然后我们可以在里面实现我们的逻辑。

        至于监听的对象,我们使用spring的初始化的一些事件,大概都有如下事件:

  1. ContextRefreshedEvent: 当应用程序上下文(ApplicationContext)初始化或刷新完成后触发。这通常发生在应用程序启动过程中,并且表示应用程序已准备好接收请求和执行业务逻辑。可以使用该事件来执行一些初始化操作,例如加载缓存数据、启动后台任务等。

  2. ContextStartedEvent: 当应用程序上下文启动时触发。这个事件在调用 ConfigurableApplicationContext 的 start() 方法后被发布。可以使用该事件来执行启动应用程序所需的特定逻辑,例如启动定时任务、启动消息监听器等。

  3. ContextStoppedEvent: 当应用程序上下文停止时触发。这个事件在调用 ConfigurableApplicationContext 的 stop() 方法后被发布。可以使用该事件来执行停止应用程序所需的清理逻辑,例如关闭连接、释放资源等。

  4. ContextClosedEvent: 当应用程序上下文关闭时触发。这个事件在调用 ConfigurableApplicationContext 的 close() 方法后被发布。可以使用该事件来执行一些最终的清理操作,例如释放数据库连接、销毁单例对象等。

  5. ServletRequestHandledEvent: 当 Spring MVC 处理完一个 HTTP 请求时触发。该事件提供了有关请求处理的详细信息,包括请求的处理时间、处理器、处理器适配器等。可以使用该事件来进行请求处理的监控、日志记录或统计信息收集等操作。

  6. ApplicationStartedEvent(Spring Boot): 当 Spring Boot 应用程序启动时触发。这个事件在 SpringApplication.run() 方法完成后被发布。可以使用该事件来执行特定于应用程序的初始化逻辑。

  7. ApplicationReadyEvent(Spring Boot): 当 Spring Boot 应用程序准备就绪时触发。这个事件表示应用程序已经启动完毕,并且已经可以提供服务。可以使用该事件来执行应用程序的后续初始化操作,例如加载数据、发送通知等。

我们这里就以 ContextRefreshedEvent 当程序上下文初始化后触发这个事件为例,代码如下:

package com.feisi.test.event;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationLister implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("容器上下文初始化完成后监听器触发,触发的事件源为:"+event.getSource());
    }
}

到这里,其实我们就完成了一个事件监听的功能。

但是!!!

我们现在思考一个问题,我们上面这个监听器,当spring上下文初始化后执行监听器,既然是这样,那么我们肯定是需要初始化一些数据,比如往redis缓存一些初始化数据等等,或者是我们自定义的一些数据。

但是上面这个示例,我们得到的参数只是事件源对象,但这个事件源对象,是spring给我们提供,我们肯定没办法自定义我们想要的数据。所以我们想要自定义我们需要的数据,那么只能自己定义一个事件源对象。

但是我们自定义了一个数据源对象,怎么才能发布这个对象呢?

这里我们再来解释一下上面那个示例的执行原理,我们的事件源对象是ContextRefreshEvent,当spring上下文对象 初始化完成后,spring就会自动帮我们发布这个事件,或者是说注册这个事件,这个时候,它就会通知所有spring容器里面实现了监听器接口(ApplicationList)并且监听的事件源对象是ContextRefreshEvent的Bean对象,也就是上面我定义的这个监听器对象,那么它满足条件就会收到这个信号,它就会被执行onApplicationEvent方法。

那么到这里,我们已经明白整个执行流程,那么我现在是不是可以自定义一个事件,并且在里面可以自定我们想要的数据,然后在我上面示例的基础上,我们把onApplicationEvent方法里面的逻辑改成发布/注册我们自定义的事件对象,然后,我们再创建一个监听器类,但是这个监听器类监听的事件源对象是我们自定义的对象,这样我们自定义的事件对象被发布后,那么监听我们自定义事件源的监听器就会被触发,然后执行里面onApplicationEvent方法,这样传来的参数就是我们自定义的事件源对象,这样该对象里面肯定就会有我们自定义的数据,我们就完成了对自定义数据的处理。

所以,在上面的基础上,我们自定义一个事件源对象。

package com.feisi.test.event;

import lombok.Data;
import org.springframework.context.ApplicationEvent;

import java.time.Clock;

public class MyApplicationEvent extends ApplicationEvent {
     private String name ;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * 必须要生成有参构造函数
     * @param source 发布这个事件的监听器
     * @param name  我们传入我们自定义的参数
     */
    public MyApplicationEvent(Object source, String name) {
        super(source);
        this.name = name ; 
    }
}

自定义事件对象完成后,我们需要把上面示例中的监听器里面的onApplicationEvent方法中的逻辑代码改一下。应该改成,当容器上下文初始化完成后,调用这个方法,然后在这个方法发布我们自定义的事件对象。

package com.feisi.test.event;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import java.util.concurrent.atomic.AtomicBoolean;

@Component
public class MyApplicationLister implements ApplicationListener<ContextRefreshedEvent> {
    @Autowired
    private ApplicationContext applicationContext ; //用于发布我们自定义的事件对象

    private AtomicBoolean flag = new AtomicBoolean(false);  // 用于防止二次触发事件

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        System.out.println("容器初始化完成后,触发事件....");
        //发布我们自定义的事件
        if(flag.compareAndSet(false,true)){  //true: 修改成功  false: 没修改
            applicationContext.publishEvent(new MyApplicationEvent(this,"zhangsan"));
        }
    }
}

 

到这里,当spring初始化完成后,就会触发监听器,然后将我们自定义的事件发布,这个时候,spring就会通知自己容器里面所有的bean,如果你实现了ApplicationEvent事件监听器接口,并且你监听的对象是刚刚已经发布的MyApplicatioinEvent对象,那么你就已经达到了触发的调用,就会被自动执行。

所以,我们现在只需要再自定义一个类实现监听器接口,并且监听我们自定义的事件对象,我们这个监听器就会被触发,我们传入的参数也是我们自定义的事件,我们就可以执行相关的逻辑代码:

package com.feisi.test.demo;

import com.feisi.test.event.MyApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component
public class MyApplicationEventListerTest implements ApplicationListener<MyApplicationEvent> {
    @Override
    public void onApplicationEvent(MyApplicationEvent event) {
        System.out.println("自定义事件源发布,开始触发事件,初始化自定义数据:"+event.getName());
    }
}

整体大概执行流程图如下:

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

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

相关文章

CTK框架(三): 插件的安装

目录 1.方式1&#xff1a;使用ctk框架工厂&#xff0c;适用于调用普通的插件 1.1.步骤 1.2.实现 2.方法2&#xff1a;使用ctk框架启动器&#xff0c;适用于需要eventadmin时 2.1.实现 3.注意事项 1.方式1&#xff1a;使用ctk框架工厂&#xff0c;适用于调用普通的插件 1…

Linux服务器应急响应(下)

目录 介绍步骤 介绍 Linux alias命令用于设置指令的别名。 用户可利用alias&#xff0c;自定指令的别名。若仅输入alias&#xff0c;则可列出目前所有的别名设置。alias的效力仅及于该次登入的操作。若要每次登入是即自动设好别名&#xff0c;可在.profile或.cshrc中设定指令…

ggplot2 缩小的、带箭头的坐标轴 | R语言

1. 效果图 左侧为DimPlot2()效果图。 右侧为DimPlot()效果图&#xff0c;原图。 2. 代码 # DimPlot with 缩小的坐标轴 # # param scObject # param reduction # param group.by # param label # param raster # param legend.position # param ... # # return # expo…

OCC开发_变高箱梁全桥建模

概述 上一篇文章《OCC开发_箱梁梁体建模》中详细介绍了箱梁梁体建模的过程。但是&#xff0c;对于实际桥梁&#xff0c;截面可能存在高度、腹板厚度、顶底板厚度变化&#xff0c;全桥的结构中心线存在平曲线和竖曲线。针对实际情况&#xff0c;通过一个截面拉伸来实现全桥建模显…

长短期记忆神经网络-LSTM回归预测-MATLAB代码实现

一、LSTM简介&#xff08;代码获取&#xff1a;底部公众号&#xff09; 长短期记忆神经网络&#xff08;Long Short-Term Memory, LSTM&#xff09;是一种循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;的变体。相比于传统的RNN&#xff0c;LSTM能够更好…

nvidia-smi 随机掉卡,error,禁用GSP功能

问题 NVIDIA 驱动中默认开启加载GPU卡的GSP功能&#xff0c;会随机导致在执行nvidia-smi命令的时候读取GPU卡为ERR状态&#xff0c;或者导致smi命令卡死&#xff1b; 如下图&#xff0c;以A800为例&#xff0c;Centos系统&#xff1b; 涉及到的包含以下型号的GPU卡&#xff…

C#中chart绘制曲线

官网资料&#xff1a;Chart 类 (System.Windows.Forms.DataVisualization.Charting) | Microsoft Learn 类的 Chart 两个重要属性是 Series 和 ChartAreas 属性&#xff0c;这两个属性都是集合属性。 Series集合属性存储Series对象&#xff0c;这些对象用于存储要显示的数据以…

2024年全新deepfacelive简易版-傻瓜式使用教程-采用AI换脸软件deepfacelive

# 2024年全新deepfacelive简易版-傻瓜式使用教程-采用AI换脸软件deepfacelive # 下载安装deepfacelive 官网下载&#xff1a; https://github.com/iperov/DeepFaceLive/ 下载好后放在至少有 30G以上的盘&#xff0c; # 启动使用-设置中文 解压好后&#xff0c;双击.bat批处理…

GPT系列之:GPT-1,GPT-2,GPT-3详细解读

一、GPT1 论文&#xff1a;Improving Language Understanding by Generative Pre-Training 链接&#xff1a;https://cdn.openai.com/research-covers/languageunsupervised/language_understanding_paper.pdf 启发点&#xff1a;生成loss和微调loss同时作用&#xff0c;让下…

java常用集合方法

目录 一、Iterator接口二、Iterable接口三、Collection接口四、Collection与Iterable关系 一、Iterator接口 Iterator 是一个集合迭代器接口&#xff0c;它提供了以下方法&#xff1a; 判断迭代器中是否还拥有元素&#xff0c;有则返回true&#xff0c;否则返回false boolean …

实验八 输入/输出流

实验目的及要求 目的&#xff1a;通过实验掌握java提供的输入/输出包中类的使用&#xff0c;特别是一些常用的类的方法的使用&#xff0c;运用流的概念实现对象的序列化。 要求&#xff1a; &#xff08;1&#xff09;编写程序使用BufferedReader和BufferedWriter对文件进行…

Java高级编程—网络编程(完整详解,包括UDP通信方式、TCP通信方式、TCP三次握手、TCP四次挥手,附有代码+案列)

文章目录 二十九.网络编程29.1 概述29.2 InetAddress29.3 UDP通信29.3.1 UDP通信发送数据29.3.2 UDP通信接收数据29.3.3 Test 29.4 UDP的三种通信方式29.4.1 单播29.4.2 组播29.4.3 广播 29.5 TCP通信29.6 TCP通信三次握手29.7 TCP通信四次挥手29.8 Test29.8.1 多次发送 二十九…

Java数据结构(九)——选择排序、堆排序

文章目录 选择排序算法介绍代码实现复杂度和稳定性 堆排序算法介绍代码实现复杂度和稳定性 选择排序 算法介绍 选择排序是一种简单直观的排序算法。它的工作原理是&#xff1a;首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放到排序序列的起始位置…

【二分查找】锦集

二分查找锦集 二分前言1. 二分查找1.1 题目来源1.2 题目描述1.3 代码展示 2. 在排序数组中查找元素的第一个和最后一个位置2.1 题目来源2.2 题目描述2.3 解题分析 3. 搜索插入位置3.1 题目来源3.2 题目描述3.3 解题分析 4. x 的平方根4.1 题目来源4.2 题目描述4.3 解题分析 5. …

Oracle rman 没有0级时1级备份和0级大小一样,可以用来做恢复 resetlogs后也可以

文档说了 full backup 不能 用于后续的level 1&#xff0c;没说level 1没有level 0 是不是level 1就是level 0&#xff1f; 1级备份变0级的原因 及 Enabling Change Tracking生效没有-CSDN博客 这个文档说明1级备份时没有找到0级就是0级备份&#xff0c;可以用来完整恢复的。…

春日美食家:SpringBoot网上订餐系统

1 绪论 1.1 研究背景 随着互联网技术的快速发展&#xff0c;网络时代的到来&#xff0c;网络信息也将会改变当今社会。各行各业在日常企业经营管理等方面也在慢慢的向规范化和网络化趋势汇合[13]。电子商务必将成为未来商务的主流&#xff0c;因此对于餐饮行业来说&#xff0c;…

51单片机的无线病床呼叫系统【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温湿度传感器模块矩阵按键时钟模块等模块构成。适用于病床呼叫系统、16床位呼叫等相似项目。 可实现基本功能: 1、LCD1602实时显示北京时间、温湿度信息、呼叫床位等信息&#xff1b; 2、DHT11采集病房温湿度信息&…

RTMP播放器延迟最低可以做到多少?

技术背景 RTMP播放器的延迟可以受到多种因素的影响&#xff0c;包括网络状况、推流设置、播放器配置以及CDN分发等。因此&#xff0c;RTMP播放器的延迟并不是一个固定的数值&#xff0c;而是可以在一定范围内变化的。 正常情况下&#xff0c;网上大多看到的&#xff0c;针对R…

【GIS系列】通过Java代码高效实现ArcGIS SDE数据库的数据叠加分析

作者&#xff1a;后端小肥肠 &#x1f347; 我写过的文章中的相关代码放到了gitee&#xff0c;地址&#xff1a;xfc-fdw-cloud: 公共解决方案 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 本文涉及GDAL及GeoTools代码实践&#xff…

计算机毕业设计选题推荐-宠物店管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…