前言

关于事件驱动模型,百度百科在有明确的解释。在JDKUtil包里抽象了事件驱动,有兴趣的朋友可以自行去看下相关类的定义。Spring事件模型ApplicationEvent是基于JDK里的事件模型,废话不多说,直接看Spring是如何定义事件模型,以及在具体业务场景中的应用。

事件

事件就是事件,鼠标点击一下算一个事件,某个按钮被点击了一下算一个点击事件,那么我订单支付了可以认为支付也算一个件事!触发了某个事件...... 等等。

抽象类ApplicationEvent承载着我们要传播的事件或者消息,白话就是说可以把某个对象用ApplicationEvent这个对象来里的Source来引用。

监听者

上面我们定义了事件,那么事件产生的一系列的效应或者是变动,那么都由这些监听者们去实现。点击下鼠标(事件),那么我记录下日志,你弹出个提示框。支付某个订单(事件),我记录下记录,他发送个支付通知...... 等等。

泛型接口ApplicationListener规定了泛型E的上边界为ApplicationEvent,意思很明确,就是给我们自定义事件用的。Spring最大的优点我认为是留给用户发挥的空间很大,就像神秘的海洋一样,它一直有你探索不完的秘密,每一次你去了解它,它都能给你带来新的事物和理解。

实战

文章SpringPlugin-Core在业务中的应用中,我们用SpringPlugin插件的方式去实现了订单的不同操作!而在某个操作里面,我们可能又要发送操作事件的通知,比如:订单支付了后,要通知打印机打印小票、微信公众号提醒支付信息等等。那么我们来实际的操作下。

定义事件源

public class OrderPayedEvent extends ApplicationEvent {

    /**
* 消息体,这里就设定为当前订单对象
*/
private final Order order; public OrderPayedEvent(Object source) {
super(source);
this.order = (Order) source;
} public Order getOrder() {
return order;
}
}

实现ApplicationEvent, 我这里Source实际传递就是Order对象,当然你也可以定义其他的多参数构造函数!

定义监听者

定义监听者的方式,Spring提供了两种,一种是接口方式,一种是注解方式。

接口方式

@Component
@Order(1)
public class OrderPayedPrinterListener implements ApplicationListener<OrderPayedEvent> { @Override
public void onApplicationEvent(OrderPayedEvent event) {
System.out.printf("【线程 - %s 】订单成功成功:第一步,打印小票%n", Thread.currentThread().getName());
}
} @Component
@Order(2)
public class OrderPayedSendMessageListener implements ApplicationListener<OrderPayedEvent> { @Override
public void onApplicationEvent(OrderPayedEvent event) {
System.out.printf("【线程 - %s 】订单成功成功:第二步,发送通知商品中心添加库存%n", Thread.currentThread().getName());
}
}

这里我定义了两个监听者,实现泛型接口ApplicationListener类型为我们刚定义的OrderPayedEvent这里加上Order注解,是因为我们有多个监听者,有此业务场景中可能会有顺序的要求!

注解方式

@Component
public class OrderPayListener { @EventListener(classes = {OrderPayedEvent.class})
public void sendTips(OrderPayedEvent event) {
System.out.printf("【线程 - %s 】订单成功成功:发送用户订单支付消息%n", Thread.currentThread().getName());
} @EventListener(classes = {OrderPayedEvent.class})
public void reward(OrderPayedEvent event) {
System.out.printf("【线程 - %s 】订单成功成功:奖励业务%n", Thread.currentThread().getName());
}
}

两种方式,各有千秋,不同业务场景选择不同实现方式即可。但注解方式是不会有排序功能的,如果你有业务有需要排序,那么建议换成接口方式

发布件事

万事具备,只欠东风。那么只要合适的位置发布事件即可,那么在上回文章中的支付成功代码加上事件即可

@Component
public class PayOperator implements OrderOperatorPlugin { //这里注入 应用上下文,可以注入 applicationEventPublisher
@Resource
ApplicationContext context; // @Resource
// ApplicationEventPublisher applicationEventPublisher; @Override
public Optional<?> apply(OrderOperatorDTO operator) {
//支付操作
//doPay() //发送事件
context.publishEvent(new OrderPayedEvent(new Order()));
return Optional.of("支付成功");
} @Override
public boolean supports(OrderOperatorDTO operatorDTO) {
return operatorDTO.getOperatorType() == OrderOperatorType.PAY;
}
}

打印如下:

【线程 - main 】订单成功成功:第一步,打印小票
【线程 - main 】订单成功成功:第二步,发送通知商品中心添加库存
【线程 - main 】订单成功成功:第四步,奖励业务
【线程 - main 】订单成功成功:第三步,发送用户订单支付消息

那么,ApplicationEvent对异步支持是怎么样的呢?

只要在启动类上加上@EnableAsync,在方法体加上@Async

再打印如下:

【线程 - task-1 】订单成功成功:第一步,打印小票
【线程 - task-2 】订单成功成功:第二步,发送通知商品中心添加库存
【线程 - task-4 】订单成功成功:第四步,奖励业务
【线程 - task-3 】订单成功成功:第三步,发送用户订单支付消息

总结

不管是EventObject,还是Observable模型,都是用来解耦代码。高内聚,低耦合的设计思想一至到现在都没有被突破过,也是我们在日常工作过程中时刻要提醒自己的编码思想。而我们更要利用好这些前人留下的精髓,应用到我们实际的业务场景中去。

[代码在GitHub](

Spring事件,ApplicationEvent在业务中的应用的更多相关文章

  1. 从spring源码汲取营养:模仿spring事件发布机制,解耦业务代码

    前言 最近在项目中做了一项优化,对业务代码进行解耦.我们部门做的是警用系统,通俗的说,可理解为110报警.一条警情,会先后经过接警员.处警调度员.一线警员,警情是需要记录每一步的日志,是要可追溯的,比 ...

  2. redis整合Spring集群搭建及业务中的使用

    1.redis安装 Redis是c语言开发的. 安装redis需要c语言的编译环境.如果没有gcc需要在线安装.yum install gcc-c++ 安装步骤: 第一步:redis的源码包上传到li ...

  3. spring事件机制

    前置知识补充: 程序里面所谓的“上下文”就是程序的执行环境,打个比方:你就相当于web程序,你的房子就相当于web程序的上下文,你可以在家里放东西,也可以取东西,你的衣食住行都依赖这个房子,这个房子就 ...

  4. Spring事件发布与监听机制

    我是陈皮,一个在互联网 Coding 的 ITer,微信搜索「陈皮的JavaLib」第一时间阅读最新文章,回复[资料],即可获得我精心整理的技术资料,电子书籍,一线大厂面试资料和优秀简历模板. 目录 ...

  5. 淘系工程师讲解的使用Spring特性优雅书写业务代码

    使用Spring特性优雅书写业务代码   大家在日常业务开发工作中相信多多少少遇到过下面这样的几个场景: 当某一个特定事件或动作发生以后,需要执行很多联动动作,如果串行去执行的话太耗时,如果引入消息中 ...

  6. spring发布和接收定制的事件(spring事件传播)

    spring发布和接收定制的事件(spring事件传播) 2012-12-26 20:05 22111人阅读 评论(2) 收藏 举报  分类: 开源技术(如Struts/spring/Hibernat ...

  7. 【Spring】关于Boot应用中集成Spring Security你必须了解的那些事

    Spring Security Spring Security是Spring社区的一个顶级项目,也是Spring Boot官方推荐使用的Security框架.除了常规的Authentication和A ...

  8. Spring事件解析

    首先介绍Spring事件相关类的关系: 其中EventListener与EventObject均是Java SE的范畴,源码如下: package java.util; public interfac ...

  9. 【转】Spring总结以及在面试中的一些问题

    [转]Spring总结以及在面试中的一些问题. 1.谈谈你对spring IOC和DI的理解,它们有什么区别? IoC Inverse of Control 反转控制的概念,就是将原本在程序中手动创建 ...

随机推荐

  1. 机器学习基本概念:batch_size、epoch、 iteration

    batch_size 单次训练用的样本数,通常为2^N,如32.64.128... 相对于正常数据集,如果过小,训练数据就收敛困难:过大,虽然相对处理速度加快,但所需内存容量增加. 使用中需要根据计算 ...

  2. 存储系统管理(一)——Linux系统的设备和分区管理

    1.设备名称的理解 /dev/sda1? sata硬盘,a1表示第一块硬盘中的第一个分区 /dev/cdrom 光驱 /dev/mapper/*? 系统中的虚拟设备 2.发现系统中的设备 ? fdis ...

  3. JS_DOM操作之绑定事件

    1 - 静态绑定:直接把事件写在标签元素中 <div id="div" onclick="foo(this)">click</div> ...

  4. python实现分水岭算法分割遥感图像

    1. 定义 分水岭算法(watershed algorithm)可以将图像中的边缘转化为"山脉",将均匀区域转化为"山谷",在这方面有助于分割目标. 分水岭算法 ...

  5. Python习题集(六)

    每天一习题,提升Python不是问题!!有更简洁的写法请评论告知我! https://www.cnblogs.com/poloyy/category/1676599.html 题目 ''' 问题1.对 ...

  6. Spring系列之集成MongoDB的2种方法

    MongoDB是最流行的NoSQL数据库,SpringBoot是使用Spring的最佳实践.今天带大家讲一讲SpringBoot集成MongoDB的两种方式,MongoDB的安装自行去官网查询,本地开 ...

  7. etcd学习(10)-etcd对比Consul和zooKeeper如何选型

    etcd选型对比 前言 基本架构和原理 etcd Consul ZooKeeper 选型对比 总结 参考 etcd选型对比 前言 对比 Consul, ZooKeeper.选型etcd有那些好处呢? ...

  8. 第一类值VS第二类值

    数据类型的分类:第一类值VS第二类值 第一类值:变量中可以存储函数,也就是说func类似int/double. 第二类值: 1.不能存储函数,不能动态创建函数,不能动态销毁函数; 2.只能存储一个指向 ...

  9. 双非Java的学习之旅以及秋招路程

    个人信息: 趁着中秋写个帖子记录一下吧.渣渣本,无实习,无高质量证书,走了很多弯路,最后选择的Java后端.现在算是半躺平了,接了几个中小厂的offer保底,20w多的薪资,后面还有几家公司接着面.不 ...

  10. Mysql实现无插入有更新(不知主键的情况下)

    网上很多资料说有两种方式 (必须现有唯一键) 1.INSERT 中ON DUPLICATE KEY UPDATE的使用 2.REPLACE的使用 通过可以得出结果: 如果a和b字段 能决定唯一 例子: ...