EventBus VS Spring Event

本地异步处理,采用事件机制 可以使 代码解耦,更易读。事件机制实现模式是 观察者模式(或发布订阅模式),主要分为三部分:发布者、监听者、事件。

Guava EventBus

Guava EventBus实现是观察者模式,用法很简单,先上代码。

不止是代码
/*** Desc: 事件对象*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HelloEvent {private String eventName;
}@Data
@NoArgsConstructor
public class WorldEvent extends HelloEvent {private int eventNo;public WorldEvent(String name, int no) {setEventName(name);setEventNo(no);}
}/*** Desc: 事件监听器,可以监听多个事件。处理方法添加 @Subscribe 注解即可。*/
public class GeventListener {/*** 监听 HelloEvent 类型及其父类型(Object)的事件*/@Subscribepublic void processEvent(HelloEvent event){System.out.println("process hello event, name:" + event.getEventName());}/*** 监听 WorldEvent 类型及其父类型(HelloEvent 和 Object)的事件*/@Subscribepublic void processWorldEvent(WorldEvent event) {System.out.println("process world eventV1, no:" + event.getEventNo() + ", name:" + event.getEventName());}/*** 注册多个监听器 监听同一事件* @param event*/@Subscribepublic void processWorldEventV2(WorldEvent event) {System.out.println("process world eventV2, no:" + event.getEventNo() + ", name:" + event.getEventName());}@Subscribepublic void processObject(Object object) {System.out.println("process common event, class:" + object.getClass().getSimpleName());}
}public class GuavaTest {public static void main(String[] args) {EventBus eventBus = new EventBus();GeventListener listener = new GeventListener();eventBus.register(listener);eventBus.post(new HelloEvent("hello"));eventBus.post(new WorldEvent("world", 23333));}
}

结果如下:

//HelloEvent被两个监听器处理(HelloEvent类及Object类的监听器)
process hello event, name:hello
process common event, class:HelloEvent
//WorldEvent被四个监听器处理(两个自己的,两个父类的)
process world eventV1, no:23333, name:world
process world eventV2, no:23333, name:world
process hello event, name:world
process common event, class:WorldEvent

由上可知:Guava EventBus把类当做事件,是以class为key注册和管理事件的,value是事件监听器的method;事件监听器只处理某一类(及其父类)事件。

事件注册与发布
//com.google.common.eventbus.EventBus#registerpublic void register(Object object) {//key为Class, value为EventSubscriber(Object target, Method method)【集合】。注意这里Multimap 为HashMultimap, 即HashMap<K, Collection<V>>Multimap<Class<?>, EventSubscriber> methodsInListener =finder.findAllSubscribers(object);subscribersByTypeLock.writeLock().lock();try {subscribersByType.putAll(methodsInListener);} finally {subscribersByTypeLock.writeLock().unlock();}}//com.google.common.eventbus.EventBus#postpublic void post(Object event) {//找到event类及其所有父类Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());boolean dispatched = false;for (Class<?> eventType : dispatchTypes) {subscribersByTypeLock.readLock().lock();try {//找到所有事件订阅者(事件监听器)Set<EventSubscriber> wrappers = subscribersByType.get(eventType);if (!wrappers.isEmpty()) {dispatched = true;for (EventSubscriber wrapper : wrappers) {//事件入队列enqueueEvent(event, wrapper);}}} finally {subscribersByTypeLock.readLock().unlock();}}//如果没有订阅者订阅此类消息,则为 DeadEventif (!dispatched && !(event instanceof DeadEvent)) {post(new DeadEvent(this, event));}dispatchQueuedEvents();}
事件隔离

多个EventBus可以隔离事件。

public class AnotherListener {/*** 监听 WorldEvent 类型及其父类型(HelloEvent 和 Object)的事件*/@Subscribepublic void processAnotherWorldEvent(WorldEvent event) {System.out.println("process another world event, no:" + event.getEventNo() + ", name:" + event.getEventName());}
}public class GuavaTest {public static void main(String[] args) {EventBus eventBus = new EventBus();GeventListener listener = new GeventListener();eventBus.register(listener);eventBus.post(new HelloEvent("hello"));EventBus anotherEventBus = new EventBus();AnotherListener anotherListener = new AnotherListener();anotherEventBus.register(anotherListener);anotherEventBus.post(new WorldEvent("AnotherWorld", 666));}
}

结果是

//eventBus结果与之前相同
process hello event, name:hello
//anotherEventBus 发布的事件,只被其注册的监听器处理
process common event, class:HelloEvent
process another world event, no:666, name:AnotherWorld

适用场景:

  • 按照类区分事件
  • 订阅 事件簇
  • 支持自定义event,可以根据event自己写分发器
  • 事件隔离

spring event

spring 新版事件机制也比较简单,看代码。

不止是代码
/*** 继承 ApplicationEvent 的事件*/
@Data
public class HelloEvent extends ApplicationEvent {private String eventName;public HelloEvent(String eventName) {super(eventName);setEventName(eventName);}
}/*** 自定义事件*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerEvent {private String name;private Boolean isCustomer;
}/*** 监听器类,spring也支持一个类中监听多个事件*/
@Component("springListener")
public class SpringListener {/*** 监听所有ApplicationEvent类型 及其子类型 的事件*/@EventListenerpublic void processApplicationEvent(ApplicationEvent event) {System.out.println("process common event, class:" + event.getClass().getSimpleName());}/*** 监听 HelloEvent类型 事件*/@EventListenerpublic void processHelloEvent(HelloEvent event) {System.out.println("process helloEvent, name:" + event.getEventName());}/*** 监听 CustomerEvent 类型事件,但是需要满足condition条件,即isCustomer=true*/@EventListener(condition = "#event.isCustomer")public void processCustomerEvent(CustomerEvent event) {System.out.println("process customer CustomerEvent, name:" + event.getName());}/*** 监听 CustomerEvent 类型事件,但是需要满足condition条件,即name="miaomiao"*/@EventListener(condition = "#event.getName().equals('miaomiao')")public void processMiaoMiaoEvent(CustomerEvent event) {System.out.println("process miaomiao's CustomerEvent, name:" + event.getName());}/*** 支持异步处理事件*/@Async@EventListenerpublic void processAsyncCustomerEvent(CustomerEvent event) {System.out.println("Async process CustomerEvent, name:" + event.getName());}
}//执行类,测试入口
@SpringBootApplication
@ComponentScan(basePackages = {"com.example.manyao.async"})
public class DemoApplication {public static void main(String[] args) throws TException {SpringApplication.run(DemoApplication.class, args);ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");String[] names = context.getBeanDefinitionNames();for(int i=0; i<names.length; i++) {System.out.println(names[i]);}System.out.println("++++++++++");context.publishEvent(new HelloEvent("helloEvent"));context.publishEvent(new CustomerEvent("customer", true));context.publishEvent(new CustomerEvent("miaomiao", false));}
}

结果是

//以下是spring上下文event,继承自 ApplicationContextEvent。 用于用户参与上下文生命周期的入口。因为是ApplicationEvent子类型,所以,由processApplicationEvent处理。
process common event, class:ContextRefreshedEvent
process common event, class:EmbeddedServletContainerInitializedEvent
process common event, class:ApplicationReadyEvent
process common event, class:ContextRefreshedEvent
//以下是上下文中的bean
springListener
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalRequiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
++++++++++
//HelloEvent 继承 ApplicationEvent,会被processApplicationEvent处理
process common event, class:HelloEvent
//监听 HelloEvent类型 的 processHelloEvent 处理
process helloEvent, name:helloEvent
//非 ApplicationEvent 的事件,则为 PayloadApplicationEvent
process common event, class:PayloadApplicationEvent
//isCustomer=true,符合processCustomerEvent处理条件
process customer CustomerEvent, name:customer
//监听CustomerEvent类型,处理结果
Async process CustomerEvent, name:customer
process common event, class:PayloadApplicationEvent
//符合processMiaoMiaoEvent条件
process miaomiao's CustomerEvent, name:miaomiao
Async process CustomerEvent, name:miaomiao
//spring 上下文事件
process common event, class:ContextClosedEvent
spring 上下文事件

上述例子中的
ContextRefreshedEvent,EmbeddedServletContainerInitializedEvent,ApplicationReadyEvent,ContextRefreshedEvent,ContextClosedEvent 等事件,都是spring上下文事件。可以通过监听这些事件,参与到spring生命周期中去。这种无侵入性交互方式,在做平台服务时,是一种很好的方式。

注册监听器

org.springframework.context.event.EventListenerMethodProcessor#processBean 将所有注解EventListener的方法,存入上下文的applicationListeners中。Listener的封装类为ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method)。
org.springframework.context.support.AbstractApplicationContext#refresh 中调用 initApplicationEventMulticaster 初始化事件发布管理器applicationEventMulticaster,然后调用registerListeners() 注册监听器。

发布事件

spring 起初只支持 ApplicationEvent类型事件,后来优化之后,支持自定义事件。自定义事件的处理,默认为PayloadApplicationEvent,相当于EventBus的DeadEvent。

//org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object, org.springframework.core.ResolvableType)protected void publishEvent(Object event, ResolvableType eventType) {Assert.notNull(event, "Event must not be null");if (logger.isTraceEnabled()) {logger.trace("Publishing event in " + getDisplayName() + ": " + event);}// Decorate event as an ApplicationEvent if necessaryApplicationEvent applicationEvent;if (event instanceof ApplicationEvent) {applicationEvent = (ApplicationEvent) event;}else {//若不是ApplicationEvent类型,则使用PayloadApplicationEvent封装applicationEvent = new PayloadApplicationEvent<Object>(this, event);if (eventType == null) {eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();}}// Multicast right now if possible - or lazily once the multicaster is initializedif (this.earlyApplicationEvents != null) {this.earlyApplicationEvents.add(applicationEvent);}else {
//核心操作,初始化 event        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);}//调用父类,发布事件// Publish event via parent context as well...if (this.parent != null) {if (this.parent instanceof AbstractApplicationContext) {((AbstractApplicationContext) this.parent).publishEvent(event, eventType);}else {this.parent.publishEvent(event);}}}
执行事件
    @Overridepublic void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));//获取事件的监听器集合,并逐个触发执行监听器for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {//异步的话,就放在线程池中执行Executor executor = getTaskExecutor();if (executor != null) {executor.execute(new Runnable() {@Overridepublic void run() {invokeListener(listener, event);}});}else {//本线程调用invokeListener(listener, event);}}}

可以看到,spring的事件机制更复杂,但是功能同样强大。
适用场景:

  • 按照类区分事件
  • 订阅 事件簇
  • 支持自定义event
  • 按照condition过滤同类型事件

比较EventBus与Spring Event

  • 使用方式比较
项目 事件 发布者 发布方法 是否异步 监听者 注册方式
EventBus 任意对象 EventBus EventBus#post 注解Subscribe方法 手动注册EventBus#register
Spring Event 任意对象 ApplicationEventPublisher ApplicationEventPublisher#publishEvent 支持同步异步 注解EventListener方法 系统注册
  • 使用场景比较
项目 事件区分 是否支持事件簇 是否支持自定义event 是否支持过滤 是否支持事件隔离 复杂程度
EventBus Class 简单
Spring Event Class 复杂

转载于:https://www.cnblogs.com/shoren/p/eventBus_springEvent.html

EventBus VS Spring Event相关推荐

  1. Spring event 使用完全指南

    说明 此篇文章以 Spring 4.2+ 为例,在此版本之前略微有不同. 笔者自2014年起开始接触和使用 Spring event,根据 Spring event 的原理开发了 JFinal-eve ...

  2. 找找 Spring Event 源码中各种设计模式的使用

    为什么80%的码农都做不了架构师?>>>    本文将按照Spring Event 是什么鬼的思路寻找 Spring 源码中与 Spring Event 有关的设计模式实现 初始化- ...

  3. Spring Event事件发布机制

    使用Spring Event优雅实现业务需求. 文章目录 一. 什么是Spring Event 二. 为什么要用Spring Event 三. 使用Spring Event实现邮件发送 一. 什么是S ...

  4. spring event的事件驱动模型的最佳实践@EventListener

    文章目录 1.spring下使用event模型 1.1 定义event 1.2 event的监听处理类.监听类实现ApplicationListener 里onApplicationEvent方法即可 ...

  5. Spring Event 业务解耦神器,刷爆了

    点击上方"芋道源码",选择"设为星标" 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | ...

  6. Spring Event + DDD = 王炸!!

    点击上方"芋道源码",选择"设为星标" 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | ...

  7. HTTP长连接、短连接使用及测试

    https://www.cnblogs.com/shoren/p/http-connection.html   漫夭   博客园   首页   新随笔   新文章   联系   订阅   管理 pos ...

  8. 微服务实践--微服务方法论00

    思想 在接收到一个新的新项目时,架构师的职责是建立项目的业务与技术实现之间的桥梁.在翻译业务到技术实现的过程中需要进行业务建模.技术设计等方面的工作.业务建模和技术设计过程中都有各自领域的知识体系.基 ...

  9. Spring Reactor教程

    在RESTful服务的世界中,实际上实际上是在幕后进行许多工作,我们通常必须在应用程序中进行很多处理,而实际上并不会影响需要发送给真实用户的响应. 可以被动地做出这些业务决策,以便它们对与应用程序交互 ...

最新文章

  1. 如何将简单CMS后台管理系统示例转换为Java、Php等不同后台语言的版本
  2. 宝塔Linux常用命令
  3. git 回退版本并强制提交
  4. python转行it好学吗-转行IT做后端开发,学python还是java?
  5. 更改计算机名引起的奇怪问题:“重新启动计算机之前控制台无法刷新”
  6. java基础----IO打印流PrintStream
  7. 本地修改远端 SAP UI5 框架文件的一个小技巧
  8. 龙芯.NET正式发布 稳步推进生态建设
  9. Android之基于xmpp openfire smack开发之smack类库介绍和使用[2]
  10. [css] 字体的粗细的属性是用哪一个?它有哪些属性值?
  11. a标签鼠标放上去变色_一切为了集齐一套装备:杜伽LEO600游戏鼠标和P300鼠标垫简评...
  12. 6复数与复变函数(六)
  13. 蓝牙学习笔记(二)——低功耗蓝牙(BLE)的体系结构
  14. SQL SERVER: 合并相关操作(Union,Except,Intersect)
  15. 如何位图转换矢量图或者数字油画底稿
  16. 爱情保卫战 - 爱情保鲜剂 语录收集
  17. EPL许可证人话翻译
  18. 计算机内存不足 无法使用,电脑内存不足怎么办,教您解决电脑内存不足
  19. mysql查询出现ambiguous的问题
  20. Android聊天室(服务器)

热门文章

  1. linux安装redis服务,配置PHP扩展
  2. 运维利器-ClusterShell集群管理操作记录
  3. Servlet第一个示例
  4. Sql Server数据库数据导入到SQLite数据库中
  5. python 初试 2
  6. 读-福瑞哈哥-的POE学习笔记
  7. Discuz!NT 在线用户功能简介
  8. asp.net小技巧:摆脱路径的困扰(三)
  9. SQLSERVER的三种备份模式
  10. ubuntu下磁道坏区的检测与修复