javascript
EventBus VS Spring Event
EventBus VS Spring Event
本地異步處理,采用事件機(jī)制 可以使 代碼解耦,更易讀。事件機(jī)制實(shí)現(xiàn)模式是 觀察者模式(或發(fā)布訂閱模式),主要分為三部分:發(fā)布者、監(jiān)聽者、事件。
Guava EventBus
Guava EventBus實(shí)現(xiàn)是觀察者模式,用法很簡單,先上代碼。
不止是代碼
/*** Desc: 事件對(duì)象*/ @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: 事件監(jiān)聽器,可以監(jiān)聽多個(gè)事件。處理方法添加 @Subscribe 注解即可。*/ public class GeventListener {/*** 監(jiān)聽 HelloEvent 類型及其父類型(Object)的事件*/@Subscribepublic void processEvent(HelloEvent event){System.out.println("process hello event, name:" + event.getEventName());}/*** 監(jiān)聽 WorldEvent 類型及其父類型(HelloEvent 和 Object)的事件*/@Subscribepublic void processWorldEvent(WorldEvent event) {System.out.println("process world eventV1, no:" + event.getEventNo() + ", name:" + event.getEventName());}/*** 注冊(cè)多個(gè)監(jiān)聽器 監(jiān)聽同一事件* @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));} }結(jié)果如下:
//HelloEvent被兩個(gè)監(jiān)聽器處理(HelloEvent類及Object類的監(jiān)聽器) process hello event, name:hello process common event, class:HelloEvent //WorldEvent被四個(gè)監(jiān)聽器處理(兩個(gè)自己的,兩個(gè)父類的) 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把類當(dāng)做事件,是以class為key注冊(cè)和管理事件的,value是事件監(jiān)聽器的method;事件監(jiān)聽器只處理某一類(及其父類)事件。
事件注冊(cè)與發(fā)布
//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 {//找到所有事件訂閱者(事件監(jiān)聽器)Set<EventSubscriber> wrappers = subscribersByType.get(eventType);if (!wrappers.isEmpty()) {dispatched = true;for (EventSubscriber wrapper : wrappers) {//事件入隊(duì)列enqueueEvent(event, wrapper);}}} finally {subscribersByTypeLock.readLock().unlock();}}//如果沒有訂閱者訂閱此類消息,則為 DeadEventif (!dispatched && !(event instanceof DeadEvent)) {post(new DeadEvent(this, event));}dispatchQueuedEvents();}事件隔離
多個(gè)EventBus可以隔離事件。
public class AnotherListener {/*** 監(jiān)聽 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));} }結(jié)果是
//eventBus結(jié)果與之前相同 process hello event, name:hello //anotherEventBus 發(fā)布的事件,只被其注冊(cè)的監(jiān)聽器處理 process common event, class:HelloEvent process another world event, no:666, name:AnotherWorld適用場景:
- 按照類區(qū)分事件
- 訂閱 事件簇
- 支持自定義event,可以根據(jù)event自己寫分發(fā)器
- 事件隔離
spring event
spring 新版事件機(jī)制也比較簡單,看代碼。
不止是代碼
/*** 繼承 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; }/*** 監(jiān)聽器類,spring也支持一個(gè)類中監(jiān)聽多個(gè)事件*/ @Component("springListener") public class SpringListener {/*** 監(jiān)聽所有ApplicationEvent類型 及其子類型 的事件*/@EventListenerpublic void processApplicationEvent(ApplicationEvent event) {System.out.println("process common event, class:" + event.getClass().getSimpleName());}/*** 監(jiān)聽 HelloEvent類型 事件*/@EventListenerpublic void processHelloEvent(HelloEvent event) {System.out.println("process helloEvent, name:" + event.getEventName());}/*** 監(jiān)聽 CustomerEvent 類型事件,但是需要滿足condition條件,即isCustomer=true*/@EventListener(condition = "#event.isCustomer")public void processCustomerEvent(CustomerEvent event) {System.out.println("process customer CustomerEvent, name:" + event.getName());}/*** 監(jiān)聽 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());} }//執(zhí)行類,測試入口 @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));} }結(jié)果是
//以下是spring上下文event,繼承自 ApplicationContextEvent。 用于用戶參與上下文生命周期的入口。因?yàn)槭茿pplicationEvent子類型,所以,由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,會(huì)被processApplicationEvent處理 process common event, class:HelloEvent //監(jiān)聽 HelloEvent類型 的 processHelloEvent 處理 process helloEvent, name:helloEvent //非 ApplicationEvent 的事件,則為 PayloadApplicationEvent process common event, class:PayloadApplicationEvent //isCustomer=true,符合processCustomerEvent處理?xiàng)l件 process customer CustomerEvent, name:customer //監(jiān)聽CustomerEvent類型,處理結(jié)果 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:ContextClosedEventspring 上下文事件
上述例子中的
ContextRefreshedEvent,EmbeddedServletContainerInitializedEvent,ApplicationReadyEvent,ContextRefreshedEvent,ContextClosedEvent 等事件,都是spring上下文事件。可以通過監(jiān)聽這些事件,參與到spring生命周期中去。這種無侵入性交互方式,在做平臺(tái)服務(wù)時(shí),是一種很好的方式。
注冊(cè)監(jiān)聽器
org.springframework.context.event.EventListenerMethodProcessor#processBean 將所有注解EventListener的方法,存入上下文的applicationListeners中。Listener的封裝類為ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method)。
org.springframework.context.support.AbstractApplicationContext#refresh 中調(diào)用 initApplicationEventMulticaster 初始化事件發(fā)布管理器applicationEventMulticaster,然后調(diào)用registerListeners() 注冊(cè)監(jiān)聽器。
發(fā)布事件
spring 起初只支持 ApplicationEvent類型事件,后來優(yōu)化之后,支持自定義事件。自定義事件的處理,默認(rèn)為PayloadApplicationEvent,相當(dāng)于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);}//調(diào)用父類,發(fā)布事件// 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);}}}執(zhí)行事件
@Overridepublic void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));//獲取事件的監(jiān)聽器集合,并逐個(gè)觸發(fā)執(zhí)行監(jiān)聽器for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {//異步的話,就放在線程池中執(zhí)行Executor executor = getTaskExecutor();if (executor != null) {executor.execute(new Runnable() {@Overridepublic void run() {invokeListener(listener, event);}});}else {//本線程調(diào)用invokeListener(listener, event);}}}可以看到,spring的事件機(jī)制更復(fù)雜,但是功能同樣強(qiáng)大。
適用場景:
- 按照類區(qū)分事件
- 訂閱 事件簇
- 支持自定義event
- 按照condition過濾同類型事件
比較EventBus與Spring Event
- 使用方式比較
| EventBus | 任意對(duì)象 | EventBus | EventBus#post | 是 | 注解Subscribe方法 | 手動(dòng)注冊(cè)EventBus#register |
| Spring Event | 任意對(duì)象 | ApplicationEventPublisher | ApplicationEventPublisher#publishEvent | 支持同步異步 | 注解EventListener方法 | 系統(tǒng)注冊(cè) |
- 使用場景比較
| EventBus | Class | 是 | 是 | 否 | 是 | 簡單 |
| Spring Event | Class | 是 | 是 | 是 | 否 | 復(fù)雜 |
轉(zhuǎn)載于:https://www.cnblogs.com/shoren/p/eventBus_springEvent.html
總結(jié)
以上是生活随笔為你收集整理的EventBus VS Spring Event的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Yii2 事件学习笔记
- 下一篇: 结构体序列为JSON