Spring中的事件机制

news2024/12/26 0:04:33

文章目录

  • 摘要
  • 正文
    • 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

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

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

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

相关文章

Codeforces Round 919 (Div. 2)

Problem - A - Codeforces n个约束条件 a x 求出满足n个约束条件的整数的个数 大于等于x&#xff0c;取最大的 小于等于x&#xff0c;取最小的 然后不等于x的&#xff0c;记录在区间范围内的个数&#xff0c;减去这些 #include<bits/stdc.h> #define endl \n #define …

【前后端的那些事】开源!treeSelect树形结构数据展示

文章目录 tree-selector1. 新增表单组件2. 在父组件中引用3. 父组件添加新增按钮4. 树形组件4.1 前端代码4.2 后端代码 前言&#xff1a;最近写项目&#xff0c;发现了一些很有意思的功能&#xff0c;想写文章&#xff0c;录视频把这些内容记录下。但这些功能太零碎&#xff0c…

快速前端开发01

前端开发 1 前端开发1.快速开发网站2.浏览器能识别的标签2.1 编码&#xff08;head&#xff09;2.2 title&#xff08;head&#xff09;2.3 标题2.4 div和span2.4.5 超链接2.4.6 图片小结2.4.7 列表2.4.8 表格2.4.9 input系列&#xff08;7个&#xff09;2.4.10 下拉框2.4.11 多…

PIFA天线设计经验分享

一、PIFA天线模型分析 从IFA天线的Smith圆图可以看出&#xff0c;其阻抗图随着频率的变化范围十分大&#xff0c;从端口Matrix data中的阻抗数据可以看到这一点。对于WIFI 2.4G频段的应用IFA输入阻抗频宽大致可以满足要求&#xff0c;但是对于其他带宽较宽的应用&#xff0c;该…

Hive 数据迁移

一、需求 同步集团的数据到断直连环境。 二、思路 三、同步数据&#xff08;方案&#xff09; 1、环境&#xff1a;断直连模拟环境 2、操作机器&#xff1a;ETL 机器 XX.14.36.216 3、工作路径&#xff1a;cd /usr/local/fqlhadoop/hadoop/bin 4、执行命令&#xff1a; 命令…

优思学院|质量管理应该看哪些书最好?

很多学员问我们&#xff0c;在探索质量管理博大精深的知识之旅中&#xff0c;应该看哪些书最好&#xff1f;我们推荐学员了解质量管理和精益六西格玛方法的发展史&#xff0c;从中了解质量管理思维的演变&#xff0c;及后再了解质量管理的工具和方法论&#xff08;包括从PDCA、…

系统性学习vue-vue组件化编程

vue组件化编程 对组件的理解使用组件创建组件注册组件编写组件标签注意 组件的嵌套VueComponent构造函数Vue实例与组件实例(vm与vc)一个重要的内置关系单文件组件(项目使用) 对组件的理解 就是将可以复用的模块提取为独立个体, 解决依赖关系混乱,复用率不高的问题 组件: 实现应…

CTF CRYPTO 密码学-2

题目名称&#xff1a;enc 题目描述&#xff1a; 字符 ZZZZ X XXZ ZZ ZXZ Z ZXZ ZX ZZX XXX XZXX XXZ ZX ZXZZ ZZXZ XX ZX ZZ 分析 此字段是由Z和X组成的字符&#xff0c;联想到莫斯密码是由.和-组成的所以接下来可以尝试莫斯密码解题 解题过程&#xff1a; Step1&#xff1a;…

AI编程可视化Java项目拆解第二弹,AI辅助生成方法流程图

之前分享过一篇使用 AI 可视化 Java 项目的文章&#xff0c;同步在 AI 破局星球、知乎、掘金等地方都分享了。 原文在这里AI 编程&#xff1a;可视化 Java 项目 有很多人感兴趣&#xff0c;我打算写一个系列文章拆解这个项目&#xff0c;大家多多点赞支持~ 今天分享的是第二…

四、任意文件读取漏洞

一、介绍 解释&#xff1a;任意文件读取漏洞就其本身来说就是&#xff0c;攻击者绕过网站防御者设置的防御&#xff0c;读取到了正常使用者不应该读取到的内容。网站开发者使用不同的语言&#xff0c;任意文件读取漏洞利用方式就不同。 二、不同开发语言的不同漏洞点 1.PHP …

(更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)

A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数&#xff08;2009-2023年&#xff09;参考《经济研究》中方先明&#xff08;2023&#xff09;的做法&#xff0c;将华证ESG评级进行赋值&#xff0c;指标包含C、CC、CCC、B、BB、BBB、A、AA、AAA共9个等级&#xff0c;…

使用Go语言的HTTP客户端和服务器

使用Go语言进行HTTP客户端和服务器开发是一种高效且强大的方式。Go语言的标准库提供了对HTTP协议的全面支持&#xff0c;使得创建HTTP客户端和服务器变得简单。 首先&#xff0c;让我们来看一下如何创建一个简单的HTTP服务器。在Go中&#xff0c;可以使用net/http包来创建HTTP…

抖音弹幕玩法汉字找不同让鼠标指针自动漂浮的实现原理及代码

如下图&#xff0c;抖音直播间弹幕互动玩法&#xff0c;为了增强用户的视觉感知体验&#xff0c;在里面加了一个鼠标&#xff0c;来让用户感知到自己在操作。下一节我们将背景音乐也给加上去。 我们实现的方案是用anime.js动画&#xff0c;来让一个图片在指定区域范围内随机漂浮…

WebGL开发智慧城市应用

在使用WebGL实现智慧城市应用时&#xff0c;需要考虑一系列的问题&#xff0c;以确保系统的性能、安全性和用户体验。以下是在开发WebGL智慧城市应用时需要注意的问题&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;…

从源码中分析SDS相较于C字符串的优势

文章目录 前言Type && EncodingsdsencodingcreateStringObjectcreateEmbeddedStringObject总结 createRawStringObject总结 createStringObjectFromLongDouble总结 createStringObjectFromLongLongWithOptions总结 相关操作sdscatlen总结 阈值44sds VS C字符串 前言 从…

加密经济学:Web3时代的新经济模型

随着Web3技术的迅猛发展&#xff0c;我们正迈入一个全新的数字经济时代。加密经济学作为这一时代的核心&#xff0c;不仅在数字货币领域崭露头角&#xff0c;更是重新定义了传统经济模型&#xff0c;为我们开启了一个充满创新和机遇的新纪元。 1. 去中心化的经济体系 Web3时代…

12.1、2、3-同步状态机的结构以及Mealy和Moore状态机的区别

同步状态机的结构以及Mealy和Moore状态机的区别 1&#xff0c;介绍Mealy型状态机和Moore型状态机的两种结构2&#xff0c;设计高速电路的方法 由于寄存器传输级&#xff08;RTL&#xff09;描述的是以时序逻辑抽象所得到的有限状态机为依据&#xff0c;因此&#xff0c;把一个时…

【嘉立创EDA-PCB设计指南】1.PCB基本概念及原理图绘制

前言&#xff1a;本文详解PCB基本概念以及实现MCU最小系统原理图的绘制&#xff08;原理图包括MCU芯片GD32F103C8T6、外部晶振、输出端口、USB输入口、5v转3v3稳压输出、复位按键、唤醒按键、LED&#xff09;。为本专栏后面章节实现PCB绘制做准备。 最终绘制的原理图如下所示&…

LinkedList ArrayDeque源码阅读

文章目录 LinkedList简介LinkedList例子LinkedList继承结构LinkedList代码分析成员变量方法 ArrayDeque简介ArrayDeque继承结构ArrayDeque代码分析总结参考链接 本人的源码阅读主要聚焦于类的使用场景&#xff0c;一般只在java层面进行分析&#xff0c;没有深入到一些native方法…

Java零基础教学文档servlet(3)

【AJax】 1.传统开发模式的不足 传统开发模式基于浏览器数据传输功能,页面填写数据/展示数据。浏览器通过访问一个URL地址&#xff0c;将页面的数据提交给服务器。服务器将需要展示的数据返回给浏览器&#xff0c;浏览器再进行数据解析&#xff0c;将数据呈现在用户面前。这种…