使用事件驅動進行代碼解耦-Spring篇

什麼是事件驅動模型

事件驅動模型也就是咱們常說的觀察者,或者發佈-訂閱模型;理解它的幾個關鍵點:java

  1. 首先是一種對象間的一對多的依賴關係;
  2. 當一個對象的狀態發生變化時,觀察者(訂閱者)都獲得通知並作相應的處理;
  3. 觀察者如何處理,目標無需干涉,鬆散耦合了它們之間的關係。

常見問題

好比說訂單狀態變化的時候,可以通知到郵件服務,短信服務,積分變化等等; 若是你是個新手,想象一下你去實現這個業務的代碼怎麼去實現?確定是一個OrderService裏面引入積分Service,短信Service,郵件Service,還有不少不少Service,可能還要調用第三方接口。是否是發現問題所在了,Service耦合嚴重,又是還會出現循環引用的問題,代碼超長,以致於不方便維護。spring

從如上例子能夠看出,應該使用一個觀察者來解耦這些Service之間的依賴關係,如圖:
設計模式

圖中增長了一個Listener來解耦OrderService和其餘Service,即註冊成功後,只須要通知相關的監聽器,不須要關心它們如何處理,處理起來很是容易。這就是一個典型的事件處理模型-觀察者模式,解耦目標對象和它的依賴對象,目標只須要通知它的依賴對象,具體怎麼處理,依賴對象本身決定。好比是異步仍是同步,延遲仍是非延遲等。
其實上邊其實也使用了DIP(依賴倒置原則),依賴於抽象,而不是具體。
仍是就是使用了IOC思想,即之前主動去建立它依賴的Service,如今只是被動等待別人註冊進來。
主要目的是:鬆散耦合對象間的一對多的依賴關係。併發

經常使用事件驅動模型

  1. 設計模式裏面的觀察者模式app

  2. JDK觀察者模式異步

  3. JavaBean事件驅動ide

  4. spring事件驅動測試

  5. ......ui

JavaBean事件驅動

JavaBean規範提供了一種監聽屬性變化的事件驅動模型,提供操做JavaBean屬性的類PropertyChangeSupport,PropertyEditorSupport以及PropertyChangeListener支持,PropertyEditorSupport就是目標,而PropertyChangeListener就是監聽器。this

Spring提供的事件

Spring提供的事件驅動模型/觀察者抽象:首先看一下Spring提供的事件驅動模型體系圖,


 

具體表明者是ApplicationEvent:
一、其繼承自JDK的EventObject,JDK要求全部事件將繼承它,並經過source獲得事件源,好比咱們的AWT事件體系也是繼承自它;
二、系統默認提供了以下ApplicationEvent事件實現:

只有一個ApplicationContextEvent,表示Spring容器事件,且其又有以下實現:

  • ContextStartedEvent:Spring容器啓動後觸發的事件;
  • ContextStoppedEvent:Spring容器中止後觸發的事件;
  • ContextRefreshedEvent:Spring容器初始化或刷新完成後觸發的事件;
  • ContextClosedEvent:Spring容器關閉後觸發的事件;

注:org.springframework.context.support.AbstractApplicationContext抽象類實現了LifeCycle的start和stop回調併發布ContextStartedEvent和ContextStoppedEvent事件。

事件發佈

具體表明者是:ApplicationEventPublisher及ApplicationEventMulticaster,系統默認提供了以下實現:

一、ApplicationContext接口繼承了ApplicationEventPublisher,並在AbstractApplicationContext實現了具體代碼,實際執行是委託給ApplicationEventMulticaster(能夠認爲是多播):

protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    //省略部分代碼

    if (event instanceof ApplicationEvent) {
        applicationEvent = (ApplicationEvent)event;
    } else {
        applicationEvent = new PayloadApplicationEvent(this, event);
        if (eventType == null) {
            eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
        }
    }

    if (this.earlyApplicationEvents != null) {
        this.earlyApplicationEvents.add(applicationEvent);
    } else {
        this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
    }

    if (this.parent != null) {
        if (this.parent instanceof AbstractApplicationContext) {
            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
        } else {
            this.parent.publishEvent(event);
        }
    }

}

咱們經常使用的ApplicationContext都繼承自AbstractApplicationContext,如ClassPathXmlApplicationContext、XmlWebApplicationContext等,因此自動擁有這個功能。

二、ApplicationContext自動到本地容器裏找一個名字爲」applicationEventMulticaster「的ApplicationEventMulticaster實現,若是沒有本身new一個SimpleApplicationEventMulticaster,代碼以下:

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
    if (beanFactory.containsLocalBean("applicationEventMulticaster")) {
        this.applicationEventMulticaster = (ApplicationEventMulticaster)beanFactory.getBean("applicationEventMulticaster", ApplicationEventMulticaster.class);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    } else {
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton("applicationEventMulticaster", this.applicationEventMulticaster);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug("Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [" + this.applicationEventMulticaster + "]");
        }
    }

}

其中SimpleApplicationEventMulticaster發佈事件的代碼以下:

public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
    Iterator var4 = this.getApplicationListeners(event, type).iterator();
    while(var4.hasNext()) {
        ApplicationListener<?> listener = (ApplicationListener)var4.next();
        Executor executor = this.getTaskExecutor();
        if (executor != null) {
            executor.execute(() -> {
                this.invokeListener(listener, event);
            });
        } else {
            this.invokeListener(listener, event);
        }
    }
}

能夠看到若是給它一個executor(java.util.concurrent.Executor),它就能夠異步支持發佈事件了,不然就是同步發送。因此咱們發送事件只須要經過ApplicationContext.publishEvent便可,不必再建立本身的實現了。除非有必要。 

監聽器

具體表明者是:ApplicationListener。其繼承自JDK的EventListener,JDK要求全部監聽器將繼承它。

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E var1);
}

它只提供了onApplicationEvent方法,咱們須要在該方法實現內部判斷事件類型來處理,或者指定某一個事件類型(泛型,實現ApplicationListener),並無提供按順序觸發監聽器的語義,因此Spring提供了另外一個接口SmartApplicationListener:

public interface Ordered {
    int getOrder();
}

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
    boolean supportsEventType(Class<? extends ApplicationEvent> var1);
    boolean supportsSourceType(@Nullable Class<?> var1);
}

該接口支持判斷的事件類型、目標類型,及執行順序。

另外,google guava事件機制使用介紹見文章:使用事件驅動進行代碼解耦-Guava篇

Spring事件機制示例

本示例模擬訂單生成與更新時會發送短信與站內信簡單業務場景demo

1. 自定義一些接口以及POJO

/**
 * 業務事件發佈者
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
public interface BizEventPublisher {

     /**
      * 發佈同步事件
      * @param eventData
      */
     void publishEvent(Object eventData);

     /**
      * 發佈異步事件
      * @param eventData
      */
     void publishEventAsync(Object eventData);

}


/**
 * 業務事件發佈者
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
public interface BizEventPublisher {

     /**
      * 發佈同步事件
      * @param eventData
      */
     void publishEvent(Object eventData);

     /**
      * 發佈異步事件
      * @param eventData
      */
     void publishEventAsync(Object eventData);

}

/**
 * 業務事件類型
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
public enum BizEventType {

    ORDER_CREATE("訂單-建立"),
    ORDER_UPDATE("訂單-修改"),
    ;

    String describe;
    BizEventType(String describe) {
        this.describe = describe;
    }

}

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 * 測試訂單類
 */
@Data
public class Order {
    private long orderId;
    private long userId;
    public Order(long orderId, long userId) {
        this.setOrderId(orderId);
        this.setUserId(userId);
    }
}

2. 自定義業務事件,包含事件通用數據屬性

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 * 自定義應用業務事件
 * @param <S>
 */
@Data
public class BizEvent<S> extends ApplicationEvent implements EventExecutor<S> {

    private BizEventType eventType;
    private S data;

    private BizEvent(BizEventType eventType, S data) {
        super(eventType);
        this.eventType = eventType;
        this.data = data;
    }

    public static<S> BizEvent of(BizEventType eventType, S data) {
        return new BizEvent(eventType, data);
    }

    @Override
    public void executeEvent(Consumer<S> executor) {
        executor.accept(data);
    }

}

3. 業務事件發佈配置管理

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 * 業務事件發佈配置管理
 */
@Slf4j
@Component
public class BizEventPublisherConfiguration implements BizEventPublisher{

    /** 注入事件發佈管理器,用於發佈自定義業務事件 */
    @Autowired
    private ApplicationEventPublisher eventPublisher;

    /** 異步事件執行線程池 */
    private ExecutorService executor = new ThreadPoolExecutor(
        Runtime.getRuntime().availableProcessors(),
        Runtime.getRuntime().availableProcessors() * 2,
        5L,
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(),
        new ThreadFactoryBuilder().setNameFormat("spring-event-executor-pool-%d").build()
    );

    @Override
    public void publishEvent(Object eventData) {
        eventPublisher.publishEvent(eventData);
    }

    @Override
    public void publishEventAsync(Object eventData) {
        executor.submit(() -> eventPublisher.publishEvent(eventData));
    }
}

4. 業務事件監聽配置管理

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 * 業務事件監聽配置管理
 */
@Slf4j
@Component
public class BizEventListenerConfiguration implements ApplicationListener<BizEvent> {

    @Autowired
    private TestMsgService msgService;

    /**
     * 收到事件執行
     * @param bizEvent
     */
    @Override
    public void onApplicationEvent(BizEvent bizEvent) {
        switch (bizEvent.getEventType()) {
            // 訂單建立,發送短信,.......
            case ORDER_CREATE: bizEvent.executeEvent((data) -> {
                msgService.sendPhoneMsg((Order) data);
            });break;
            // 訂單修改,站內信提醒,.......
            case ORDER_UPDATE: bizEvent.executeEvent((data) -> {
                msgService.sendWebMsg((Order) data);
            });break;

            default: bizEvent.executeEvent((data) -> {
                log.info("onApplicationEvent bizEvent.data = {}", data);
            });
        }
    }

}

5. 定義兩個測試service,TestOrderService 和 TestMsgService

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
@Slf4j
@Service
public class TestOrderService {

    @Autowired
    private BizEventPublisher bizEventPublisher;

    public void create(Order order) {
        ......
        log.info("TestOrderService create order = {}", order);
        bizEventPublisher.publishEvent(BizEvent.of(BizEventType.ORDER_CREATE, order));
    }

    public void update(Order order) {
        ......
        log.info("TestOrderService update order = {}", order);
        bizEventPublisher.publishEventAsync(BizEvent.of(BizEventType.ORDER_UPDATE, order));
    }

}

/**
 * @author dongsilin
 * @version 1.0
 * @date 2019/1/24
 */
@Slf4j
@Service
public class TestMsgService {


    public void sendPhoneMsg(Order order) {
        ......
        log.info("TestMsgService sendPhoneMsg order = {}", order);
    }

    public void sendWebMsg(Order order) {
        ......
        log.info("TestMsgService sendWebMsg order = {}", order);
    }
}

此處TestOrderService 中並無強制依賴注入TestMsgService,而是經過發佈事件的方式將事件數據發佈到事件管理中心,由事件管理者來統一管理事件處理方式,解決了各個業務service嚴重耦合的場景,實現軟件開發中的「高內聚-低耦合」原則。

經過如上,大致瞭解了Spring的事件機制,可使用該機制很是簡單的完成事件流程。

相關文章
相關標籤/搜索