從spring框架中的事件驅動模型出發,優化實際應用開發代碼


一、事件起源

  相信很多人在使用spring框架進行開發時,都會遇到這樣的需求:在spring啟動后,立即加載部分資源(例如:spring啟動后立刻加載資源初始化到redis中)。當我去解決這個問題時發現,springboot啟動過程中會有事件驅動模型的具體實現,共有兩種實現:

  1)第一種實現,具體代碼如下:

import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

@Component  //將監聽者交由spring管理
public class StartedListener implements ApplicationListener<ApplicationStartedEvent> {
  //實現了ApplicationListener接口並監聽ApplicationStartedEvent事件 覆蓋當事件發生的方法 @Override public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) { System.out.println("i am started is ready ...");//只需改為調用具體的業務方法即可 } }

  2)第二種實現,具體代碼如下:

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component  //將監聽者交由spring管理
public class CommandListener implements CommandLineRunner {
  //實現了CommandLineRunner接口並覆蓋run()方法 @Override public void run(String... args) throws Exception { System.out.println("i am commandline runner is ready ...");//將輸出改為調用具體的業務方法即可 } }

  第一種和第二種之間存在聯系,稍后會介紹兩者之間的聯系和區別,我們更加直觀的可以看到第一種方法中的具體實現。

  發現,1、只需要實現ApplicationListener這個接口,2、並聲明泛型-監聽的具體事件,3、然后覆蓋onApplicationEvent()方法三步走戰略即可。

  還發現一個優勢:如果一個將一個具體業務形象化為一個“事件”,例如:用戶注冊或者是用戶下訂單,這種具體的業務如果改造成事件的話,就可以實現解耦,方便以后添加用戶注冊或者下訂單后的操作,舉個栗子:

  買家用戶下訂單 -> 1、扣去商品表中的庫存數量 -> 2、短信通知買家用戶下單成功 -> 3、通知商家用戶物流發貨 -> 4、微信通知買家下單成功 等 后續操作

  如果現在,我們需要對該業務代碼進行變化,我們需要增加一個需求變化:需要在買家用戶下訂單成功后,給買家用戶進行:小程序通知,當你面對這個需求時,很直觀的感覺應該是去直接在具體的業務代碼下,加上一個小程序通知即可,可是忽略了之前可能有對應的邏輯操作,這個時候可能還需要花費一些時間,把之前的代碼給看清楚,避免自己維護代碼時出現問題。

  這個時候如果我們直接去用事件驅動模型這種方式,來思考我們的業務,完全可以把買家用戶下訂單這個作為一個事件存在,這樣其他的操作就是並行的操作,可以獨立存在,如果需要直接添加就可以了。

 

  我們先來看一下原來未改造之前的代碼:

import com.shuwen.demo.service.OrderService;
import org.springframework.stereotype.Service;

@Service
public class OrderServiceImpl2 implements OrderService {
    
    @Override
    public void saveOder() {
        //1、創建訂單
        System.out.println("訂單創建成功");
        //2、發送短信通知
        System.out.println("用戶您好,恭喜您搶購成功~來自短信");
        //3、發送微信通知
        System.out.println("用戶您好,恭喜您搶購成功~來自微信");
        //4、發送小程序通知
        System.out.println("用戶您好,恭喜您搶購成功~來自小程序");
    }
}

 

  再來對比一下改造之后的代碼:

    a)首先聲明一個事件:OrderCreateEvent

import org.springframework.context.ApplicationEvent;
//繼承ApplicationEvent即可
public class OrderCreateEvent extends ApplicationEvent {
   //默認覆蓋 
  public OrderCreateEvent(Object source) { super(source); } }

      b)當買家下訂單成功后,就需要創建並發布一個事件

import com.shuwen.demo.event.OrderCreateEvent;
import com.shuwen.demo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    ApplicationContext applicationContext;
    @Override
    public void saveOder() {
        //1、創建訂單
        System.out.println("訂單創建成功");
        //創建訂單創建的事件
        OrderCreateEvent orderCreateEvent = new OrderCreateEvent("order create is success");
        //利用applicationContext將事件發布
        applicationContext.publishEvent(orderCreateEvent);
        //2、發送短信通知
        //3、發送微信通知
        //4、發送小程序通知
    }
}

    c)我們就可以模仿spring進行監聽事件發生,然后完成后續操作

      1、進行短信通知

import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component  //交給spring管理 實現接口並監聽事件源
public class MessageListener implements ApplicationListener<OrderCreateEvent> {
    @Async  //可以異步執行
    @Override  //覆蓋方法 即可實現具體的業務操作
    public void onApplicationEvent(OrderCreateEvent orderCreateEvent) {
        System.out.println(orderCreateEvent.getSource()+",message is sending");
    }
}

      2、進行微信通知

import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class WeixinListener implements ApplicationListener<OrderCreateEvent> {
    @Async
    @Override
    public void onApplicationEvent(OrderCreateEvent orderCreateEvent) {
        System.out.println(orderCreateEvent.getSource()+",weixin is sending");
    }
}

      3、這時候如果新需求是增加:小程序通知,我們直接添加一個監聽者 即可。

import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class LittleProListener implements ApplicationListener<OrderCreateEvent> {
    @Async
    @Override
    public void onApplicationEvent(OrderCreateEvent orderCreateEvent) {
        System.out.println(orderCreateEvent.getSource()+",xiao cheng xu is sending ...");
    }
}

  能夠寫出這樣代碼的人,你要珍惜他,因為當你維護他的項目時,很輕松,不用擔心出現問題,也不必費時看原來的邏輯。

  這時候,可能就有人有這樣的問題,這如果存在邏輯順序怎么處理這個問題呢?

  如果存在這種邏輯順序,我們又該如何處理,spring自然給我們提供了方法,哈哈,這就是spring的魅力~

  它的名字很有意思:SmartApplicationListener  smart是不是一下就聰明、伶俐了很多~

  我們先看看它是如何實現具體的業務的,方便大家更好的理解:

    a)微信通知

import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component  //實現smart接口
public class SmsListener implements SmartApplicationListener {
    @Override  //聲明支持的事件類型
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return aClass == OrderCreateEvent.class;
    }

    @Override  //參數的類型
    public boolean supportsSourceType(Class<?> sourceType) {
        return sourceType == String.class;
    }
    //order 確定優先級的順序 數值越小 優先級越高
    @Override  //優先級數值
    public int getOrder() {
        return 5;
    }

    @Override  //具體的業務實現
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println(applicationEvent.getSource()+",sms is sending smart");
    }
}

    b)微信通知

import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Async
@Component  //實現smart接口
public class WeixinListenerSmart implements SmartApplicationListener {
    @Override  //聲明支持的事件類型
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return aClass == OrderCreateEvent.class;
    }

    @Override  //聲明支持的參數類型
    public boolean supportsSourceType(Class<?> sourceType) {
        return sourceType == String.class;
    }

    @Override  //聲明優先級的大小 越小越高
    public int getOrder() {
        return 2;
    }

    @Override  //覆蓋方法 實現具體的業務方法
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println(applicationEvent.getSource()+",weixin is sending smart");
    }
}

    c)小程序通知

import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component;

@Component  //實現smart接口
public class LittleProListenerSmart implements SmartApplicationListener {
    @Override  //聲明支持的事件類型
    public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
        return aClass == OrderCreateEvent.class;
    }

    @Override  //聲明支持的參數類型
    public boolean supportsSourceType(Class<?> sourceType) {
        return sourceType == String.class;
    }

    @Override  //聲明該監聽者的優先級
    public int getOrder() {
        return 6;
    }

    @Override  //具體的業務實現
    public void onApplicationEvent(ApplicationEvent applicationEvent) {
        System.out.println(applicationEvent.getSource()+",xiao cheng xu is sending smart");
    }
}

  大家一定要記得清楚喲,order值越小,優先級越高。

  那么下面,我們要去追根溯源一下,看看spring的事件驅動模式的具體實現及相關的原理,讓我們有一個更加深入的理解。

  二、spring提供的事件驅動模型/觀察者抽象

  我們首先需要了解一下spring的體系結構圖:

    1)事件的具體代表者是: ApplicationEvent,其繼承自JDK的EventObject,JDK要求所有事件將繼承它,並通過source得到事件源;
    系統默認提供了如下ApplicationEvent事件實現:
    
    只有一個ApplicationContextEvent,表示ApplicationContext容器事件,且其又有如下實現:
      a)ContextStartedEvent:ApplicationContext啟動后觸發的事件;(目前版本沒有任何作用)
      b)ContextStoppedEvent:ApplicationContext停止后觸發的事件;(目前版本沒有任何作用)
      c)ContextRefreshedEvent:ApplicationContext初始化或刷新完成后觸發的事件;(容器初始化完成后調用)
      d)ContextClosedEvent:ApplicationContext關閉后觸發的事件;(如web容器關閉時自動會觸發spring容器的關閉,如果是普通java應用,需要調用ctx.registerShutdownHook();注冊虛擬機關閉時的鈎子才行)
      注:org.springframework.context.support.AbstractApplicationContext抽象類實現了LifeCycle的start和stop回調並發布ContextStartedEvent和ContextStoppedEvent事件;但是無任何實現調用它,所以目前無任何作用。
    2)事件發布者,具體代表者: ApplicationEventPublisher及ApplicationEventMulticaster,
    系統默認提供了如下實現:
      a) ApplicationContext接口繼承了ApplicationEventPublisher,並在AbstractApplicationContext實現了具體代碼,實際執行是委托給ApplicationEventMulticaster(可以認為是多播):
 public void publishEvent(ApplicationEvent event) {
        //省略部分代碼
        }
        getApplicationEventMulticaster().multicastEvent(event);
        if (this.parent != null) {
            this.parent.publishEvent(event);
        }
}

     我們常用的ApplicationContext都繼承自AbstractApplicationContext,如ClassPathXmlApplicationContext、XmlWebApplicationContext等。所以自動擁有這個功能。

      b)  ApplicationContext自動到本地容器里找一個名字為”“的ApplicationEventMulticaster實現,如果沒有自己new一個SimpleApplicationEventMulticaster。其中SimpleApplicationEventMulticaster發布事件的代碼如下:

public void multicastEvent(final ApplicationEvent event) {
        for (final ApplicationListener listener : getApplicationListeners(event)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    public void run() {
                        listener.onApplicationEvent(event);
                    }
                });
            }
            else {
                listener.onApplicationEvent(event);
            }
        }
}

   大家可以看到如果給它一個executor(java.util.concurrent.Executor),它就可以異步支持發布事件了。否則就是通過發送。

   所以我們發送事件只需要通過ApplicationContext.publishEvent即可,沒必要再創建自己的實現了。除非有必要。

  3)監聽者,具體實現者: ApplicationListener, 其繼承自JDK的EventListener,JDK要求所有監聽器將繼承它;

 ApplicationListener接口:

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

   其只提供了onApplicationEvent方法,我們需要在該方法實現內部判斷事件類型來處理,也沒有提供按順序觸發監聽器的語義,所以Spring提供了另一個接口,SmartApplicationListener:

public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
   //支持的事件類型 
  boolean supportsEventType(Class<? extends ApplicationEvent> var1);   //支持的參數類型 default boolean supportsSourceType(@Nullable Class<?> sourceType) { return true; }   //優先級的大小 default int getOrder() { return 2147483647; } }

  時光匆匆不留人,望你我皆有所收獲。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM