基于Spring的發布訂閱模式 EventListener

基于Spring的發布訂閱模式在我們使用spring開發應用時,經常會碰到要去解耦合一些依賴調用 , 比如我們在做代碼的發布流程中,需要去通知相關的測試,開發人員關注發布中的錯誤信息 。而且通知這個操作又不希望強耦合在主業務流程中,這個時候我們很容易就想到了觀察者設計模式,而spring恰好提供了事件-監聽機制,讓我們看一下他們是具體怎么實現的吧 。
事件-監聽機制:

  1. 首先是一種對象間的一對多的關系;最簡單的如交通信號燈,信號燈是目標(一方),行人注視著信號燈(多方);
  2. 當目標發送改變(發布),觀察者(訂閱者)就可以接收到改變;
  3. 觀察者如何處理(如行人如何走,是快走/慢走/不走,目標不會管的),目標無需干涉;所以就松散耦合了它們之間的關系 。
Spring提供的事件驅動模型/觀察者抽象
基于Spring的發布訂閱模式 EventListener

文章插圖
其實整個模型就有三個角色,事件,目標(發布者),監聽者,我們看一下spring中如何實現這三者
事件:具體代表者是:ApplicationEvent:
1、 我們可以看到spring中ApplicationEvent該抽象類繼承自JDK的EventObject 。JDK要求所有事件將繼承它,并通過source得到事件源 。
package org.springframework.context;import java.util.EventObject;/** * Class to be extended by all application events. Abstract as it * doesn't make sense for generic events to be published directly. * * @author Rod Johnson * @author Juergen Hoeller */public abstract class ApplicationEvent extends EventObject { /** use serialVersionUID from Spring 1.2 for interoperability */ private static final long serialVersionUID = 7099057708183571937L; /** System time when the event happened */ private final long timestamp; /*** Create a new ApplicationEvent.* @param source the object on which the event initially occurred (never {@code null})*/ public ApplicationEvent(Object source) {super(source);this.timestamp = System.currentTimeMillis(); } /*** Return the system time in milliseconds when the event happened.*/ public final long getTimestamp() {return this.timestamp; }}
目標(發布者)具體代表者是具體代表者是:ApplicationEventPublisher及ApplicationEventMulticaster 。ApplicationContext該接口繼承了ApplicationEventPublisher,并在AbstractApplicationContext實現了具體代碼 , 實際執行是委托給ApplicationEventMulticaster(可以認為是多播):
ApplicationContext繼承自ApplicationEventPublisher
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}ApplicationEventPublisher定義了publishEvent方法
public interface ApplicationEventPublisher {/*** Notify all <strong>matching</strong> listeners registered with this* application of an application event. Events may be framework events* (such as RequestHandledEvent) or application-specific events.* @param event the event to publish* @see org.springframework.web.context.support.RequestHandledEvent*/void publishEvent(ApplicationEvent event);/*** Notify all <strong>matching</strong> listeners registered with this* application of an event.* <p>If the specified {@code event} is not an {@link ApplicationEvent},* it is wrapped in a {@link PayloadApplicationEvent}.* @param event the event to publish* @since 4.2* @see PayloadApplicationEvent*/void publishEvent(Object event);}在AbstractApplicationContext實現了具體代碼,實際執行是委托給ApplicationEventMulticaster(可以認為是多播)
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 = 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 {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);}}}ApplicationContext自動到本地容器里找一個ApplicationEventMulticaster實現,如果沒有自己new一個SimpleApplicationEventMulticaster 。

推薦閱讀