Spring中的事件机制

文章目录

  • 摘要
  • 正文
    • jdk事件
    • Spring事件
      • Spring事件监听
        • ApplicationContext主动监听
        • 注解监听
        • Bean监听
      • Spring事件发布
  • 总结

摘要

在这篇文章我们将介绍Spring的事件机制,包括Spring内置事件、自定义事件、事件监听、事件发布、事件广播机制、事件异常处理等内容。Spring事件是对JDK提供的观察者模式,结合Spring容器进行的深度扩展,了解Spring提供了那些内置事件,以及如何自定义事件可以让我们很方便的对Spring框架进行扩展,来实现更多更丰富的功能。🦾

正文

jdk事件

说到事件监听,不得不提到观察者模式,jdk提供了简单的观察者模式工具类方便我们使用,它包括 java.util.Observable类和java.util.Observer接口,其中 Observer作为观察者来监听事件, Observable作为事件发布者。简单的样例代码如下


public class JdkObserver {
    public static void main(String[] args) {
        MyObservable observable = new MyObservable();
        //监听事件
        observable.addObserver(new MyObserver());
        //发布事件
        observable.notifyObservers("测试事件发布");
    }

}

class MyObservable extends Observable{
    @Override
    public void notifyObservers(Object arg) {
        super.setChanged();
        super.notifyObservers(new EventObject(arg));
        super.clearChanged();
    }
}

class MyObserver implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        EventObject event = (EventObject) arg;
        System.out.println("收到事件:"+event);
    }
}

Spring事件

通过上例可以发现,要想监听一个事件,有三个要素,①事件本身EventObject、②发布事件的动作notifyObservers、③监听事件addObserver。Spring标准事件对Java标准事件EventObject进行了扩展,添加了一个事件发生时间戳属性,同时Spring内置了一系列的应用上下文事件,方便调用

Spring内置的上下文应用事件包括:

  • Spring应用上下文就绪事件:ContextRefreshedEvent
  • Spring应用上下文启动事件:ContextStartedEvent
  • Spring应用上下文停止事件:ContextStoppedEvent
  • Spring应用上下文关闭事件:ContextClosedEvent

以上四个内置事件都继承自 ApplicationContextEvent这个类,而 ApplicationContextEvent继承了 ApplicationEventApplicationEvent继承自 Java.util.EventObject。整体的类继承关系如下图所示

在这里插入图片描述

在实际应用中,可以监听以上四个内置事件,也可以直接监听 ApplicationEvent,实现对所有Spring事件的监听,如果以上几种都不能满足需求,也可以通过实现 ApplicationEventApplicationContextEvent来自定义事件。一个简单的自定义事件如下代码所示

public class MyCustomEvent extends ApplicationContextEvent {
    public MyCustomEvent(Object source) {
        super(source);
    }
}

Spring事件监听

在Spring中监听事件的方法有很多种,主要包括

  1. 通过ApplicationContext主动监听
  2. 通过@EventListener注解监听
  3. 注册一个ApplicationEventListener类型的Bean
ApplicationContext主动监听

通过ApplicationContext主动添加事件监听,需要能获取到 ApplicationContext这个应用上下文,最简单的方式是实现 ApplicationContextAware接口,关于 ApplicationContextAware的具体内容可以看这篇文章

@Component
public class SpringEventComponent implements ApplicationContextAware {
    private Logger logger= LoggerFactory.getLogger(SpringEventComponent.class);

    private ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if(applicationContext instanceof AbstractApplicationContext){
            ((AbstractApplicationContext) applicationContext).addApplicationListener(new ApplicationListener<ApplicationEvent>() {
                @Override
                public void onApplicationEvent(ApplicationEvent event) {
                    logger.info("接收到事件:"+event);
                }
            });
        }
        this.context=applicationContext;
    }
}
注解监听

通过注解注册事件监听的方式很简单,只需要在方法上使用 @EventListener注解即可

@Component
public class SpringEventComponent implements ApplicationContextAware {
    private Logger logger= LoggerFactory.getLogger(SpringEventComponent.class);

    private ApplicationContext context;
    
    @EventListener
    public void annoEventListener(ApplicationEvent event){
        logger.info("Annotation接收到事件:"+event);
    }
}

基于注解的这种方式,还支持通过 @Async注解实现异步事件

@EventListener
@Async
public void annoEventListener(ApplicationEvent event){
    logger.info("Annotation接收到事件:"+event);
}

//需要在主类种启动异步支持
@SpringBootApplication
@EnableAsync
public class SpringNotesApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringNotesApplication.class, args);
    }
}

同步和异步的区别在于,同步的方式事件监听在Main线程,异步的方式是在一个新的线程,可以通过日志打印查看具体的执行线程,异步的效果如下

2024-01-15  11:41:00.854 [task-3] INFO  top.sunyog.spring.notes.event.SpringEventComponent - Annotation接收到事件:org.springframework.boot.context.event.ApplicationStartedEvent[source=org.springframework.boot.SpringApplication@6feb8a8c]

同步的效果如下

2024-01-15  11:43:24.469 [main] INFO  top.sunyog.spring.notes.event.SpringEventComponent - Annotation接收到事件:org.springframework.boot.context.event.ApplicationStartedEvent[source=org.springframework.boot.SpringApplication@726a6b94]
Bean监听

通过注册一个 ApplicationListener类型的Bean也可以实现事件的监听。注册Bean的方式也是多种多样的,如:①可以通过@Bean注解注册一个Bean;②通过ApplicationContext注册Bean;③通过自定义BeanDefinition注册Bean。要了解Bean的配置或注册方式,可以查看这篇文章

以下代码是通过@Bean注解注册一个ApplicationListener

@Bean
public ApplicationListener applicationListener(){
    ApplicationListener<ApplicationEvent> listener = new ApplicationListener<ApplicationEvent>(){
        @Override
        public void onApplicationEvent(ApplicationEvent event) {
            logger.info("@Bean接收到事件:"+event);
        }
    };

    return listener;
}

Spring事件发布

Spring内置了事件发布器 ApplicationEventPublisher,通过它可以实现自定义事件的发布,事件发布器可以通过依赖注入获取,也可以通过实现 ApplicationEventPublisherAware来获取。发布一个简单的自定义事件的代码如下

@Component
public class SpringEventComponent implements ApplicationEventPublisherAware, ApplicationRunner {
    private Logger logger= LoggerFactory.getLogger(SpringEventComponent.class);

    private ApplicationEventPublisher publisher;

    @Bean
    public ApplicationListener applicationListener(){
        ApplicationListener<ApplicationEvent> listener = new ApplicationListener<ApplicationEvent>(){
            @Override
            public void onApplicationEvent(ApplicationEvent event) {
                logger.info("@Bean接收到事件:"+event);
            }
        };

        return listener;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher=applicationEventPublisher;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        this.publisher.publishEvent(new MyCustomEvent("自定义事件发布ing"));
    }
}

事件监听结果为

2024-01-15  14:14:17.201 [main] INFO  top.sunyog.spring.notes.event.SpringEventComponent - @Bean接收到事件:top.sunyog.spring.notes.event.MyCustomEvent[source=自定义事件发布ing]

ApplicationEventPublisher中的 publishEvent(Objecy)方法可以发布 PayloadApplicationEvent事件,这是Spring为简化事件发布而推出的一类事件,可以直接通过 ApplicationEventPublisher#publishEvent(Object)方法发布该类型的事件,PayloadApplicationEvent事件发布时除了事件本身外,还包含一个事件消息,publishEvent(Object )方法中的Object就是这个消息。

@Component
public class SpringEventComponent implements ApplicationEventPublisherAware, ApplicationRunner {
    private Logger logger= LoggerFactory.getLogger(SpringEventComponent.class);

    private ApplicationEventPublisher publisher;
    
    @EventListener
    public void annoEventListener(PayloadApplicationEvent event){
        Object payload = event.getPayload();
        logger.info("@EventListener接收到payload事件:"+event+",msg: "+payload);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.publisher=applicationEventPublisher;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        //
        this.publisher.publishEvent("payload事件发布ing,这是一个消息");
    }
}

事件监听结果如下

2024-01-15  15:02:40.463 [main] INFO  top.sunyog.spring.notes.event.SpringEventComponent - @EventListener接收到payload事件:org.springframework.context.PayloadApplicationEvent[source=org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@6e35bc3d, started on Mon Jan 15 15:02:39 CST 2024],msg: payload事件发布ing,这是一条消息

除了 ApplicationEventPublisher之外,在Spring中还可以通过 ApplicationEventMulticaster广播器来发布事件,广播器是一个内置的SpringBean,可以通过依赖查找或依赖注入的方式获得,广播器发布事件的方式如下

public class SpringEventComponent implements ApplicationRunner {
    @Resource
    private ApplicationEventMulticaster multicaster;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        this.multicaster.multicastEvent(new MyCustomEvent("自定义事件广播ing"));
    }
}

事件监听结果

2024-01-15  14:20:38.008 [main] INFO  top.sunyog.spring.notes.event.SpringEventComponent - @Bean接收到事件:top.sunyog.spring.notes.event.MyCustomEvent[source=自定义事件广播ing]

这里的时间广播器 ApplicationEventMulticaster是一个接口,在Spring中,所有的事件都通过它来广播出来,具体的类继承关系图包括

SimpleApplicationEventMulticaster
Executor taskExecutor
ErrorHandler errorHandler
AbstractApplicationEventMulticaster
-DefaultListenerRetriever defaultRetriever
Map retrieverCache
ConfigurableBeanFactory beanFactory
«interface»
ApplicationEventMulticaster

AbstractApplicationEventMulticaster类的 retrieverCache属性中保存了所有已注册的 ApplicationListener,在Spring的应用上下文中保存了 SimpleApplicationEventMulticaster,当事件发布时,通过这个类向所有的事件监听器广播事件,即实现了事件的监听。

在Spring4.1版本中,引入了事件异常处理机制,在 SimpleApplicationEventMulticaster类中增加了一个 setErrorHandler方法,可以用来设定事件异常时进行的操作,如事件监听时发生如下异常

@Bean
public ApplicationListener applicationListener(){
    ApplicationListener<MyCustomEvent> listener = new ApplicationListener<MyCustomEvent>(){
        @Override
        public void onApplicationEvent(MyCustomEvent event) {
            throw new RuntimeException("事件异常");
        }
    };

    return listener;
}

这时,可以通过以下方式设置事件异常处理

@Component
public class SpringEventComponent implements ApplicationEventPublisherAware, ApplicationRunner {
    private Logger logger= LoggerFactory.getLogger(SpringEventComponent.class);

    private ApplicationEventPublisher publisher;

    @Resource
    private ApplicationEventMulticaster multicaster;
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        //设置事件异常处理器
        if (this.multicaster instanceof SimpleApplicationEventMulticaster){
            ((SimpleApplicationEventMulticaster) multicaster).setErrorHandler(handler->{
                String message = handler.getMessage();
                logger.info("事件发生异常:"+message);
            });
        }
        this.publisher.publishEvent(new MyCustomEvent("自定义事件发布ing"));
    }
}

发生事件异常时,的打印结果

2024-01-15  15:25:34.890 [main] INFO  top.sunyog.spring.notes.event.SpringEventComponent - 事件发生异常:事件异常

总结

本文主要介绍了Spring中的事件机制,包括事件监听、事件发布、自定义事件等。Spring中的事件实际上是对jdk提供的观察者模式的深入扩展,结合Spring容器实现了事件的深度扩展。在Spring框架项目的开发过程中,我们可以应用事件机制,实现一些常用的功能,如对容器的不同行为(启动、停止、刷新等)进行扩展。


📩 联系方式
邮箱: qijilaoli@foxmail.com
掘金: 我的掘金
CSDN: 我的CSDN

❗版权声明
本文为原创文章,版权归作者所有。未经许可,禁止转载。更多内容请访问我的博客首页

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32 TIM输出比较、PWM波形

单片机学习&#xff01; 目录 一、输出比较简介 二、PWM简介 三、输出比较通道 3.1通用定时器的输出比较部分电路 3.2高级定时器的输出比较部分电路 四、输出模式控制器 五、PWM基本结构 六、PWM参数计算 总结 前言 文章讲述STM32定时器的输出比较功能&#xff0c;它主…

K8S中使用helm安装MinIO

注意事项 使用helm部署MinIO分为两部分 helm部署MinIO operator&#xff0c;用来管理tenant&#xff08;K8S集群中只能部署一个&#xff09;helm部署MinIO tenant&#xff0c;真实的MinIO Cluster&#xff08;K8S集群中可以部署多个&#xff09; 使用helm部署到K8S集群&…

网工每日一练(1月15日)

1.某计算机系统由下图所示的部件构成&#xff0c;假定每个部件的千小时可靠度为R&#xff0c;则该系统的千小时的可靠度为 ( D ) 。 2.以下IP地址中&#xff0c;属于网络 201.110.12.224/28 的主机IP是&#xff08; B &#xff09;。 A.201.110.12.224 B.201.110.12.238 C.20…

【文本到上下文 #5】:RNN、LSTM 和 GRU

一、说明 欢迎来到“完整的 NLP 指南&#xff1a;文本到上下文 #5”&#xff0c;这是我们对自然语言处理 &#xff08;NLP&#xff09; 和深度学习的持续探索。从NLP的基础知识到机器学习应用程序&#xff0c;我们现在深入研究了神经网络的复杂世界及其处理语言的深刻能力。 在…

如何快速部署本地训练的 Bert-VITS2 语音模型到 Hugging Face

Hugging Face是一个机器学习&#xff08;ML&#xff09;和数据科学平台和社区&#xff0c;帮助用户构建、部署和训练机器学习模型。它提供基础设施&#xff0c;用于在实时应用中演示、运行和部署人工智能&#xff08;AI&#xff09;。用户还可以浏览其他用户上传的模型和数据集…

pip与pip3的区别

pip 和 pip3 都是 Python 的包管理工具&#xff0c;用于安装第三方库。它们的区别在于&#xff1a; pip 是 Python 2 和 Python 3 通用的包管理工具&#xff0c;它可以安装适用于 Python 2 和 Python 3 的库。pip3 是专门用于 Python 3 的包管理工具&#xff0c;它只能安装适用…

2023.12.30性质

若连通图上各边的权值均不相同&#xff0c;则该图的最小生成树是唯一的。 由k算法&#xff0c;即由边从小到大的顺序构造&#xff0c;如果边权值各不相同&#xff0c;那么构造出来的最小生成树唯一&#xff0c;就是唯一的顺序&#xff0c;从小到大 关于带权无向图的最小生成…

2024年信息安全不完全预测

不需要专家预言就能知道&#xff0c;计算机安全将在2024年出现在新闻中&#xff0c;而且可能不是什么好事。但2024年网络犯罪分子将如何试图突破防御并窃取有价值的数据&#xff0c;值得我们看一看安全专家们的看法和预测。 不需要专家预言就能知道&#xff0c;计算机安全将在…

<Linux> 进程间通信

目录 前言&#xff1a; 一、进程间通信 &#xff08;一&#xff09;进程间通信目的 &#xff08;二&#xff09;进程通信的要求 &#xff08;三&#xff09;进程间通信分类 二、管道 &#xff08;一&#xff09;什么是管道 &#xff08;二&#xff09;基本原理 &#…

SQL性能分析

SQL性能分析 1、SQL执行频率 ​ MySQL 客户端连接成功后&#xff0c;通过 show [session|global] status 命令可以提供服务器状态信 息。通过如下指令&#xff0c;可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次&#xff1a; -- session 是查看当前会话 ; …

20240115如何在线识别俄语字幕?

20240115如何在线识别俄语字幕&#xff1f; 2024/1/15 21:25 百度搜索&#xff1a;俄罗斯语 音频 在线识别 字幕 Bilibili&#xff1a;俄语AI字幕识别 音视频转文字 字幕小工具V1.2 BING&#xff1a;音视频转文字 字幕小工具V1.2 https://www.bilibili.com/video/BV1d34y1F7…

python爬虫实战(10)--获取本站热榜

1. 需要的类库 import requests import pandas as pd2. 分析 通过分析&#xff0c;本站的热榜数据可以直接通过接口拿到&#xff0c;故不需要解析标签&#xff0c;请求热榜数据接口 url "https://xxxt/xxxx/web/blog/hot-rank?page0&pageSize25&type" #本…

多臂老虎机 “Multi-armed Bandits”

将强化学习与机器学习、深度学习区分开的最重要的特征为&#xff1a;它通过训练中信息来评估所采取的动作&#xff0c;而不是给出正确的动作进行指导&#xff0c;这极大地促进了寻找更优动作的需求。 1、多臂老虎机&#xff08;Multi-armed Bandits&#xff09;问题 赌场的老虎…

超简单的node爬虫小案例

同前端爬取参数一样&#xff0c;输入三个参数进行爬取 注意点也一样&#xff1a; 注意分页的字段需要在代码里面定制化修改&#xff0c;根据你爬取的接口&#xff0c;他的业务规则改代码中的字段。比如我这里总条数叫total&#xff0c;人家的不一定。返回的数据我这里是data.r…

适用于动态 IT 环境的服务器流量监控软件

服务器在网络性能中起着至关重要的作用&#xff0c;这意味着保持其最佳容量至关重要。企业需要将 AI、ML 和云技术融入其 IT 中&#xff0c;从而提供充分的敏捷性、安全性和灵活性&#xff0c;在这方面&#xff0c;服务器流量监控已成为当务之急。通过定期监控通信、跟踪流量上…

怿星科技测试实验室获CNAS实验室认可,汽车以太网检测能力达国际标准

2023年12月27日&#xff0c;上海怿星电子科技有限公司测试实验室&#xff08;下称&#xff1a;EPT LABS&#xff09;通过CNAS实验室认可批准&#xff0c;并于2024年1月5日正式取得CNAS实验室认可证书&#xff08;注册号CNAS L19826&#xff09;&#xff0c;标志着怿星科技的实验…

Notepad++编译运行C/C++程序

首先需要先下载一个C语言编译器-MinGW&#xff08;免费的&#xff09; 官网&#xff1a;http://www.mingw.org/&#xff08;加载太慢&#xff09; 我选择MinGW - Minimalist GNU for Windows download | SourceForge.net这个网址下载的 注意安装地址&#xff0c;后续配置环境…

mac上搭建 hadoop 伪集群

1. hadoop介绍 Hadoop是Apache基金会开发的一个开源的分布式计算平台&#xff0c;主要用于处理和分析大数据。Hadoop的核心设计理念是将计算任务分布到多个节点上&#xff0c;以实现高度可扩展性和容错性。它主要由以下几个部分组成&#xff1a; HDFS (Hadoop Distributed Fi…

基于SSM的流浪动物救助站

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

【漏洞复现】Sentinel Dashboard SSRF漏洞(CVE-2021-44139)

Nx01 产品简介 Sentinel Dashboard是一个轻量级的开源控制台&#xff0c;提供机器发现以及健康情况管理、监控、规则管理和推送的功能。它还提供了详细的被保护资源的实际访问统计情况&#xff0c;以及为不同服务配置的限流规则。 Nx02 漏洞描述 CVE-2021-44139漏洞主要存在于…