EventBus與Spring Event區別詳解(EventBus 事件機制,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)的事件 */ @Subscribe public void processEvent(HelloEvent event){ System.out.println("process hello event,name:" + event.getEventName()); } /** * 監聽 WorldEvent 型別及其父型別(HelloEvent 和 Object)的事件 */ @Subscribe public void processWorldEvent(WorldEvent event) { System.out.println("process world eventV1,no:" + event.getEventNo() + ",name:" + event.getEventName()); } /** * 註冊多個監聽器 監聽同一事件 * @param event */ @Subscribe public void processWorldEventV2(WorldEvent event) { System.out.println("process world eventV2,name:" + event.getEventName()); } @Subscribe public 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,name:world process hello event,name:world process common event,class:WorldEvent
由上可知:Guava EventBus把類當做事件,是以class為key註冊和管理事件的,value是事件監聽器的method;事件監聽器只處理某一類(及其父類)事件。
事件註冊與釋出
//com.google.common.eventbus.EventBus#register public 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#post public 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(); } } //如果沒有訂閱者訂閱此類訊息,則為 DeadEvent if (!dispatched && !(event instanceof DeadEvent)) { post(new DeadEvent(this,event)); } dispatchQueuedEvents(); }
事件隔離
多個EventBus可以隔離事件。
public class AnotherListener { /** * 監聽 WorldEvent 型別及其父型別(HelloEvent 和 Object)的事件 */ @Subscribe public void processAnotherWorldEvent(WorldEvent event) { System.out.println("process another world event,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型別 及其子型別 的事件 */ @EventListener public void processApplicationEvent(ApplicationEvent event) { System.out.println("process common event,class:" + event.getClass().getSimpleName()); } /** * 監聽 HelloEvent型別 事件 */ @EventListener public 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 @EventListener public 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 necessary ApplicationEvent 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 initialized if (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); } } }
執行事件
@Override public 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() { @Override public 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 | 是 | 是 | 是 | 否 | 複雜 |
更多關於EventBus與Spring Event文章大家可檢視下面的相關連結