Spring事件机制详解

Spring注解 @EventListener 的介绍与使用示例以及异常处理 将一个方法标记为监听器,用于监听应用程序事件,事件可以是 ApplicationEvent实例,也可以是其他任意的对象。 阅读详情

一、前言

  说来惭愧,对应Spring事件机制之前只知道实现 ApplicationListener 接口,就可以基于Spring自带的事件做一些事情(如ContextRefreshedEvent),但是最近看公司的wiki基于Spring事件的领域驱动才发现原来还有这么多东西。

二、订阅/发布(观察者模式)

2.1简介

Spring是基于事件驱动模型的,我们常用的MQ就是基于观察者模式设计的。
事件驱动模型也就是我们常说的观察者,或者发布-订阅模型;理解它的几个关键点:

  1. 首先是一种对象间的一对多的关系;最简单的如交通信号灯,信号灯是目标(一方),行人注视着信号灯(多方)。
  2. 当目标发送改变(发布),观察者(订阅者)就可以接收到改变。
  3. 观察者如何处理(如行人如何走,是快走/慢走/不走,目标不会管的),目标无需干涉;所以就松散耦合了它们之间的关系。

Java API实现和自定义实现观察者模式:

Java提供了两个接口java.util.Observable和java.util.Observer,代码可参考https://github.com/2YSP/design-pattern/tree/master/src/cn/sp/observer

三、Spring类图分析

在这里插入图片描述

事件

  1. ApplicationEvent 继承自 JDK 的 EventObject,JDK要求所有事件将继承它,并通过source得到事件源,比如AWT事件体系也是继承它。
  2. 系统默认提供了如下ApplicationEvent事件实现:

在这里插入图片描述

事件发布者

具体代表者是:ApplicationEventPublisher及ApplicationEventMulticaster,系统默认提供了如下实现:

ApplicationEventPublisher类图

ApplicationEventMulticaster类图

  1. ApplicationContext 接口继承了 ApplicationEventPublisher,并在 AbstractApplicationContext 实现了具体代码,实际执行是委托给ApplicationEventMulticaster(可以认为是多播)
    代码如下:

AbstractApplicationContext发布事件代码

ApplicationEventMulticaster初始化逻辑
2.根据上面代码可以看出 ApplicationContext 会自动在本地容器找一个ApplicationEventMulticaster的实现,如果没有自己new一个SimpleApplicationEventMulticaster,其中SimpleApplicationEventMulticaster发布事件的代码如下:

发布事件方法
可以看出如果给它一个executor,它就可以实现异步发布事件了,否则就是同步发送。

监听器

ApplicationListener 接口提供了onApplicationEvent方法,但是我们需要在该方法实现内部判断事件类型来处理,也没有提供按顺序触发监听器的语义,所以Spring提供了另一个接口,SmartApplicationListener:

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
     //如果实现支持该事件类型 那么返回true
    boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
  
        //如果实现支持“目标”类型,那么返回true
    boolean supportsSourceType(Class<?> sourceType);
       
     //顺序,即监听器执行的顺序,值越小优先级越高
    int getOrder();

}

源码分析到这里,下面说说怎么使用。

四、简单实现(实现ApplicationListener接口)

场景是我们保存一个订单后发布事件通知,以便做一些其他操作比如锁定商品。
订单实体类:

public class Order {

    private String orderNo;

    private String orderStatus;

    private String goods;

    private Date createTime;
	
	//省略get、set、toString方法
}

订单创建事件OrderCreateEvent

public class OrderCreateEvent extends ApplicationEvent {

    private final Order order;

    public OrderCreateEvent(Object source,Order order) {
        super(source);
        this.order = order;
    }

    public Order getOrder(){
        return order;
    }
}

OrderService

@Service
public class OrderService implements ApplicationEventPublisherAware {

    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 订单保存
     */
    public void save(){
        String orderNo = getOrderNo();
        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setOrderStatus("待付款");
        order.setCreateTime(new Date());
        order.setGoods("手机");
        System.out.println("订单保存成功:" + order.toString());

        //发布事件
        applicationEventPublisher.publishEvent(new OrderCreateEvent(this,order));
        System.out.println("================");

    }

    private String getOrderNo() {
        String millis = String.valueOf(System.currentTimeMillis());
        String str = millis.substring(millis.length() - 7, millis.length() - 1);
        return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")) + str;
    }


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

监听器OrderCreateEventListener

@Component
public class OrderCreateEventListener implements ApplicationListener<OrderCreateEvent> {


    @Override
    public void onApplicationEvent(OrderCreateEvent event) {
        System.out.printf(this.getClass().getName()+ " -- ApplicationListener 接口实现,订单号[%s]:,锁定商品[%s]\n",
                event.getOrder().getOrderNo(), event.getOrder().getGoods());
    }
}

运行测试类:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationEventDemoApplicationTests {

	@Autowired
	OrderService orderService;

	@Test
	public void contextLoads() {
		orderService.save();
	}

}

控制台打印如下则表示成功实现了监听。

订单保存成功:Order{orderNo='20190601983801', orderStatus='待付款', goods='手机', createTime=Sat Jun 01 00:23:58 CST 2019}
2019-06-01 00:23:58.069  INFO 15060 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
cn.sp.listener.OrderCreateEventListener -- ApplicationListener 接口实现,订单号[20190601983801]:,锁定商品[手机]
================

五、注解驱动@EventListener

接着上面的,自定义的监听器一定要实现ApplicationListener接口吗?
不是,Spring还提供了注解的方式 @EventListener,使用示例如下:

@Component
public class OrderCreateEventListenerAnnotation {

    @EventListener
    public void createOrderEvent(OrderCreateEvent event){
        System.out.println(this.getClass().getName()+"--订单创建事件,@EventListener注解实现,orderNo:"+event.getOrder().getOrderNo());
    }
}

注意:@EventListener有个condition属性,还可以支持条件判断(定义布尔SpEL表达式),只有满足条件才会触发,后面泛型支持那里有示例。

六、异步事件

上面的监听事件都是同步触发的,如果想异步的怎么办?
只需要两步:

  1. 启动类上添加 @EnableAsync注解,开启异步支持。
  2. 监听方法上添加 @Async注解
	@Async
    @EventListener
    public void createOrderEvent(OrderCreateEvent event){
        System.out.println(this.getClass().getName()+"--订单创建事件,@EventListener注解实现,orderNo:"+event.getOrder().getOrderNo());
    }

七、泛型支持

事件类一定要继承ApplicationEvent吗?
当然不是,我们还可以自定义泛型类实现事件调度(这个是我认为最厉害的地方了)。

  1. 写个通用泛型类事件
/**
 * 可以自定义泛型类实现事件调度
 * Created by 2YSP on 2019/5/30.
 */
public class GenericEvent<T> {

    private  T data;
    private boolean success;

    public GenericEvent(T data,boolean success){
        this.data = data;
        this.success = success;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }
}
  1. 写个具体类型的事件
public class OrderGenericEvent extends GenericEvent<Order> {

    public OrderGenericEvent(Order data, boolean success) {
        super(data, success);
    }
}
  1. 在OrderService的save()方法中发布事件

applicationEventPublisher.publishEvent(new OrderGenericEvent(order,true));

  1. 事件处理
@Component
public class OrderGenericEventListener {

    @EventListener(condition = "#event.success")
    public void orderListener(GenericEvent<Order> event){
        System.out.println(this.getClass().getName()+"--处理泛型条件事件。。。");
    }
}

测试结果表明,成功处理了事件。

success为true时
我们把发布事件的代码改为如下内容再测试,则不会收到事件通知。
applicationEventPublisher.publishEvent(new OrderGenericEvent(order,false));

八、事件传播机制

当我们监听一个事件处理完成时,还需要发布另一个事件,一般我们想到的是调用ApplicationEventPublisher#publishEvent发布事件方法,但Spring提供了另一种更加灵活的新的事件继续传播机制,监听方法返回一个事件,也就是方法的返回值就是一个事件对象。
示例代码:

 public void save(){
        String orderNo = getOrderNo();
        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setOrderStatus("待付款");
        order.setCreateTime(new Date());
        order.setGoods("手机");
        System.out.println("订单保存成功:" + order.toString());

        //发布事件
//        applicationEventPublisher.publishEvent(new OrderCreateEvent(this,order));
        applicationEventPublisher.publishEvent(order);
//        applicationEventPublisher.publishEvent(new OrderGenericEvent(order,true));
        System.out.println("================");

    }

订单监听器

@Component
public class OrderListener {

    @EventListener
    public void orderListener(Order order){
        System.out.println(this.getClass().getName() + " -- 监听一个订单");
    }

    @EventListener
    public OrderCreateEvent orderReturnEvent(Order order){
        System.out.println(this.getClass().getName() + " -- 监听一个订单,返回一个新的事件 OrderCreateEvent");
        return new OrderCreateEvent(this,order);
    }
}

启动单元测试,就会发现OrderCreateEventListener也被触发了。

在这里插入图片描述
当然还可以返回多个事件,不再举例。

九、事物事件@TransactionalEventListener

从Spring 4.2开始,框架提供了一个新的@TransactionalEventListener注解,它是@EventListener的扩展,允许将事件的侦听器绑定到事务的一个阶段。绑定可以进行以下事务阶段:

  1. AFTER_COMMIT(默认的):在事务成功后触发
  2. AFTER_ROLLBACK:事务回滚时触发
  3. AFTER_COMPLETION:事务完成后触发,不论是否成功
  4. BEFORE_COMMIT:事务提交之前触发
@TransactionalEventListener(phase = BEFORE_COMMIT)
public void txEvent(Order order) {
    System.out.println("事物监听");
}

上面代码的意思是仅当存在事件生成器正在运行且即将提交的事务时,才会调用此侦听器。并且,如果没有正在运行的事务,则根本不发送事件,除非我们通过将fallbackExecution 属性设置为true来覆盖它 ,即 @TransactionalEventListener(fallbackExecution = true)。

十、总结

基于事件驱动模型可以很方便的实现解耦,提高代码的可读性和可维护性,代码地址:https://github.com/2YSP/application-event-demo
疑问: 泛型支持那里如果不写一个类继承通用泛型事件,就跑不通这是为什么呢?
如果有人知道请告诉我,非常感谢。
参考:https://blog.youkuaiyun.com/sun_shaoping/article/details/84067446

spring的Applicationcontext对事件的监听,实现类似MQ的效果 spring的Applicationcontext对事件的监听,在一个applicationContext范围内,实现类似MQ的效果 立即下载

相关推荐

2025年有道翻译网页版JS逆向实战:从抓包到AES解密,手把手教你搞定sign签名

本文详细解析2025年有道翻译网页版的JS逆向实战,从抓包分析到AES解密全流程。通过实战演示签名生成机制和AES-CBC解密技术,帮助开发者破解有道翻译的反爬系统,实现高效数据获取。重点涵盖动态密钥处理、错误重试机制及反反爬策略,为开发者提供完整的技术解决方案。

weixin_30379531的博客 505

事件驱动(Spring自带的@EventListener)和消息驱动(RabbitMq)

文章目录1.两者的区别:2.Spring下的事件监听的使用:2.1 发布事件2.1 定义事件源2.3 监听事件@EventListener2.4 监听事件时的事务隔离 1.两者的区别: 消息驱动和事件驱动很类似,都是先有一个事件,然后产生一个相应的消息,再把消息放入消息队列,由需要的项目获取。他们的区别是消息是谁产生的 消息驱动:鼠标管自己点击不需要和系统有过多的交互,消息由系统(第三方)循环检测,来捕获并放入消息队列。消息对于点击事件来说是被动产生的,高内聚。 事件驱动:鼠标点击产生点击事件后要向系统发送

weixin_42030357的博客 4940

Unity跨平台存储避坑指南:persistentDataPath权限真相

Application.persistentDataPath是Unity中用于持久化数据的核心路径,表面提供跨平台抽象,实则底层访问机制因操作系统而异。其原理并非简单路径映射,而是受Android Scoped Storage、iOS Data Protection、Windows组策略及macOS隐私权限等多层安全模型动态约束。技术价值在于保障用户数据长期可靠存储,但若忽略平台差异,极易引发‘本地正常、真机崩溃’‘iOS可存、Android报错’等线上事故。典型应用场景包括游戏存档、离线缓存、自定义资源包

weixin_32073537的博客 350

spring事件监听event(默认的mq)

项目中的业务难免是相互关联的,但是代码中我们应该尽量做到低耦合,常见的做法就是引入mq来作为松耦合的一种手段;其实最常见的解耦和就是接口了,MVC直接通过接口(约定好的一种规则)相互调用,都是解耦的一种体现;还有就是springbean aop等 也就是IOC 都是解耦和的场景;通常mq是我们的选择,但是当仅仅是为了解耦的简单业务场景,其实完全没必要引入mq,而spring中的event则就可以实现解耦~

学海无涯,我用JAVA 1054

长期换衣行人重识别(Long-Term Clothes-Changing Person Reid)数据集汇总

长期换衣行人重识别任务中目前常用的数据集总览目前换衣数据集的主要问题数据集换衣/总体情况集统计DeepChange(2021)LaST(2021)COCAS(2020)VC-Clothes(2020)LTCC(2020)PRCC(2019)Celebrities-reID(2019) 总览 在长期换衣行人重识别的任务当中,数据集的获取(尤其是对于学术环境下)是一个难点,主要的困难有以下几点: 采集大规模的数据集比较困难。 采集不同的衣服的数据集比较困难。 采集不同衣服不同角度的数据集比较困难。 目前也有

KingsMan666的博客 8378

2026华为OD机试(JAVA&Go)真题【E卷+A卷+B卷+C卷+D卷】目录+在线OJ

华为OD机试真题题库【E卷+A卷+B卷+C卷+D卷】目录,Java实现。

MISAYAONE的博客 34万+

Spring系列第27篇:spring事件机制详解

月底免费送书活动,这两天是最后的机会,大家尽快参与!面试官:看你是85年的我:嗯,35了面试官:那应该经验很丰富了,那我们来聊聊spring吧我:好,这块我用了10几年了,你随便问吧面试...

路人甲Java 4万+

YOLO26涨点改进 | 独家创新,注意力改进篇 | AAAI 2025 | YOLO26引入 DRM 防御优化模块,进行特征优化/特征增强,助力目标检测、图像分类、图像分割有效涨点

本文提出了一种基于DRM防御优化模块改进的YOLO26网络模型,显著提升了目标检测的鲁棒性和特征提取能力。DRM模块通过优化特征提取过程、增强抗噪声能力和补充细节信息,有效提高了模型在复杂场景下的检测精度和定位准确度。该模块可集成于UNet等主干网络,采用Transformer结构实现特征精炼,在对抗攻击环境下仍能保持稳定的融合效果。实验表明,改进后的模型在红外与可见光图像融合任务中表现出色,同时提升了下游任务性能。文章详细介绍了DRM模块的结构、作用机制及实现代码,并提供了两种YOLO26改进方案.

Ai缝合怪、助力小伙伴高效发论文 143

Spring 之 事件机制详解

概念 Spring事件机制分为事件发布器(EventPublisher)、事件监听器(EventListener)和事件多播器(ApplicationEventMulticaster)。Spring事件机制对应常用设计模式之观察者模式,主要就是用来解耦。 Spring 的 ApplicationContext 提供了支持事件和代码中监听器的功能。我们可以创建 bean 用来监听在 ApplicationContext 中发布的事件。ApplicationEvent 类和在ApplicationContext

岁月安然 1514

【全志V821_FoxPi】6-2 IMX219 MIPI摄像头适配

全志V821芯片适配IMX219 MIPI摄像头(2lane)教程,基于FoxPi开发板V1.2 SDK。文章详细介绍了IMX219的技术参数(800万像素、1/4英寸CMOS、支持1080P@30fps输出)及模组接口原理图。重点展示了设备树配置方法,包括PIO引脚定义(TWI0使用PA3/PA4,复位引脚PA2)、TWI0节点设置(100kHz时钟)以及VIND0节点中sensor0的完整配置(I2C地址0x20、MCLK0、RAW格式输出等)。配置过程强调需注意IMX219的器件地址转换(0x10→0

第四维度的博客 2251

BES 平台 SDK之 美格信Dongle 连接

最近项目到了试产阶段,需要用到美格信的Dongel 来测试ANC 相关功能。在此简单介绍下如何连接Dongel 的操作步骤。

谢文浩博客 925

Spring及Springboot事件机制详解

Spring提供了强大的扩展体系,其中本篇介绍的事件机制是其扩展体系中一个主要分支。本篇先介绍Spring事件机制原理,再总结下spring和spring boot启动过程的主要事件,接着通过示例展示了自定义监听器的4种主要的注册方式,最后通过示例介绍了自定义事件的发布方式。

chyohn的博客 1722

【WRF-Chem】预处理工具(Preprocessors)总结

预处理工具(Preprocessors)总结

WW、forever的博客 1179

Spring的事件机制详解

事件传播 ApplicationContext基于Observer模式(java.util包中有对应实现),提供了针对Bean的事件传播功能。通过Application. publishEvent方法,我们可以将事件通知系统内所有的ApplicationListener。事件传播的一个典型应用是,当Bean中的操作发生异常(如数据库连接失败),则通过事件传播机制通知异常监听器进行处理。在笔者的一个项目中,就曾经借助事件机制,较好的实现了当系统异常时在监视终端上报警,同时发送报警SMS至管理员手机的功能。 .

杜海的博客 902

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Spring 事件广播机制详解

写这篇文章的初衷源自对 Spring 框架中事件机制的好奇心。在编写观察者模式代码示例时,我突然想起了 Spring 框架中支持多事件监听的特性,例如ContextClosedEvent、ContextRefreshedEvent、ContextStartedEvent等等。传统的观察者模式通常是基于单一事件的,但 Spring 框架却提供了一种更为灵活的机制,可以处理多个不同类型的事件。因此,我决定深入研究 Spring 框架中多事件监听的实现机制,并将我的学习总结记录下来。

MarkusZhang的博客 1696

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Spring 事件处理机制详解,带你吃透 Spring 事件

Spring 事件处理基于 Java 观察者模式扩展。Spring 应用上下文中发布了各种事件,此外 Spring 还允许我们发送和处理自定义的事件,本篇将对 Spring 的事件机制使用及其实现进行详细介绍。

大鹏的博客 5325

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

Spring Boot 事件机制详解

Spring Boot 事件机制详解

嗨,欢迎来到我的 优快云 博客小天地!一名深耕多年的技术发烧友。在这里,我将把日常工作中积累的宝贵经验,从复杂架构设计的精妙之处,到代码优化的实战技巧,毫无保留地分享给大家。 7252

Spring Boot事件机制详解

事件驱动架构(Event-Driven Architecture, EDA)是一种软件设计模式,其中系统组件通过事件的发布与订阅进行通信。在Spring Boot中,事件机制为应用程序提供了松耦合的组件间通信方式,使得发布者无需关心谁在监听,监听者也无需了解事件来源

AWen_Jack的博客 1874

Spring之事件机制详解

可以看到,这个接口定义了增删查监听者的方法,所以,监听者列表的维护通过这个接口实现。需要注意的是这个接口还定义了multicastEvent方法。通过这个方法,将事件传给监听器。所以这个类,将事件和监听器,连接在一起。这里采用的是中介者模式,这个接口就是中介者角色。 ApplicationEventPublisher: Spring设计的事件发布类,我们看其源码: public interface ApplicationEventPublisher { /** Notify all matching l

m0_63176399的博客 142

Spring系列之事件机制详解

面试官:看你是85年的 我:嗯,35了 面试官:那应该经验很丰富了,那我们来聊聊spring吧 我:好,这块我用了10几年了,你随便问吧 面试官:Spring中的事件用过么? 我:用过 面试官:可以介绍一下为什么需要使用事件么? 我:使用事件的模式可以对系统进行解耦,事件源发布一个事件,事件监听器可以消费这个事件,而事件源不用关注发布的事件有哪些监听器,这可以可以对系统进行解耦 面试官:Spring事件的实现有几种方式? 我:整体来说2种方式,第一种是通过接口的方式,第二种是在方法上使用注

azhou的代码园 2422
上一篇: springboot-jpa报错No identifier specified for entity:
下一篇: 细说分布式锁
2YSP
博客等级 码龄10年 12粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值