Spring中的事件監聽機制

1.Spring Event

  • 在設計模式中,觀察者模式能夠算得上是一種很是經典的行爲設計模式,事件---事件發佈者---事件監聽者是事件驅動模型在設計層面的體現.
  • Spring容器中經過ApplicationEvent類和ApplicationListener接口來處理事件的.若是某個bean實現ApplicationListener接口並被部署到容器中,那麼每次對應的ApplicationEvent被髮布到容器中都會通知該bean,這是典型的觀察者模式.
  • Spring的事件默認是同步的,即調用#publishEvent方法發佈事件後,它會處於阻塞狀態,直到#onApplicationEvent接受到事件並處理返回以後才繼續執行下去,這種單線程同步的好處是能夠進行事務管理.
  • 系統默認提供的容器事件的異步發佈機制參數

image.png


2.Spring提供的事件驅動模型

  • 事件java

    • 其繼承自JDKEventObject,JDK要求全部的事件都繼承它,並經過#resource獲得事件源,咱們的AWT事件體系也是繼承自它.
    • 系統默認提供以下ApplicationEvent事件:

image.png

  • 目標(事件發佈者)設計模式

    • 具體表明:ApplicationEventPublisherApplicationEventMulticaster,系統提供以下實現:

image.png

  • ApplicationContext接口繼承了ApplicationEventPublisher,並在abstractApplicationContext#publishEvent方法實現具體代碼,實際執行委託給ApplicationEventMulticaster#multicastEvent方法.
  • ApplicationContext#initApplicationEventMulticaster方法會自動到本地容器裏找一個名爲ApplicationEventMulticaster的實現,若是沒有就new一個SimpleApplicationEventMulticaster.
  • 能夠看到若是提供一個executor,它就能夠異步支持發佈事件,不然爲同步發佈.

image.png

  • 監聽器異步

    • 具體表明:ApplicationListener源碼分析

      • 其繼承自JDKEventListener,JDK要求全部的監聽器將繼承它,好比咱們的AWT事件體系也是繼承自它.
      • ApplicationListener接口:其只提供了#onApplicationEvent方法,咱們須要在該方法實現內部判斷事件類型來處理,若想提供順序觸發監聽器的語義,則可使用另外一個接口:SmartApplicationListener

image.png


3.Spring事件驅動案例

現假設一個用戶註冊的案例場景.用戶註冊後,系統須要給用戶發送郵件告知用戶註冊是否成功,須要給用戶初始化積分,後續可能會添加其餘的操做,如再發一條手機短信等,但願程序具備拓展性符合開閉原則.spa

  • 若是不使用事件驅動,代碼可能會像這個樣子:
    image.png

要說代碼有什麼問題其實也不算,由於大多數人在開發時第一直覺都會這麼寫,寫同步代碼.可是這麼寫,實際上並非特別符合隱含的設計需求,假設增長更多的註冊項Service,咱們須要修改#register方法,並讓UserService注入對應的Service.而實際上register並不關心這些"額外"的操做,如何將這些代碼抽取出去,這時能夠考慮Event機制.線程

1.無序

  • 定義用戶註冊事件設計

    • ApplicationEvent是由Spring提供的全部Event類的基類,這裏爲了簡單隻傳遞name.

image.png

  • 定義用戶註冊服務(事件發佈者)3d

    • 服務交給Spring容器管理.
    • ApplicationEventPublishAware是由Spring提供的用於Service注入ApplicationEventPublisher事件發佈器的接口.使用這個接口,咱們的Service就擁有發佈事件的能力了.
    • 用戶註冊後,再也不是顯示調用其餘的業務Service,而是發佈一個用戶註冊事件

image.png

  • 定義郵件服務,積分服務,其餘服務(事件訂閱者)code

    • 事件訂閱者的服務一樣須要託管於Spring容器
    • ApplicationListener<E extends ApplicationEvent>接口是Spring提供的事件訂閱者必須實現的接口,咱們通常把Service關心的事件做爲泛型傳入.
    • 事件處理:ApplicationEvent#getSource拿到事件的具體內容,本例中爲name.

image.png
image.png


2.有序

  • 當發佈多個事件的時候,他們的順序是無序的.若是要控制順序,則監聽器Service須要實現Order接口或者使用SmartApplicationEventListener.
  • 經過Spring事件驅動模型,咱們完成了註冊服務和其餘服務之間的解耦,這也是事件驅動的最大特性之一,若後續要新增其餘操做,只須要添加相應的事件訂閱者便可.

image.png

  • supportsEventType:用於指定支持的事件類型,只有支持的才調用#onApplicationEvent方法.
  • supportsSourceType:支持的目標類型,只有支持的才調用#onApplicationEvent方法.
  • getOrder:順序越小優先級越高,監聽器默認優先級爲7.

4.Spring對Event的註解支持

  • 註解式的事件發佈者:Spring4.2以後,ApplicationEventPublisher自動被注入到容器中,再也不須要顯示實現Aware接口

image.png

  • 註解式的事件訂閱者:@EventListener註解完成了ApplicationListener<E extends ApplicationEvent>接口的使命.

image.png


5.Spring對異步事件機制的支持

  • java配置經過@EnableAsync模塊註解開啓異步支持,使用@Async註解對須要異步的監聽器進行標註.

image.png


6.Spring對事件監聽機制的分析

1.Spring事件監聽模型

  • 事件(ApplicationEvent):繼承JDKEventObjectSpring項目中能夠繼承ApplicationEvent來定義本身的事件
  • 事件發佈者(Application):實現這個接口,就可使得Spring組件有發佈事件的能力,ApplicationContext實現了此接口,所以,發佈事件的方式有以下幾種:對象

    • 經過實現ApplicationEventPublisherAware接口,獲取注入的ApplicationPublisher對象來進行事件發佈.
    • 經過實現ApplicationContextAware接口,獲取注入的ApplicationContext來進行事件發佈.
    • 經過@autowired註解直接注入ApplicationEventPublisher或者ApplicationContext對象來調用AbstractApplicationContext#publishEvent來委託ApplicationEventMulticaster進行事件發佈

image.png

2.Spring事件監聽流程分析

  • 經過源碼分析,在AbstractApplicationContext類中,定義了針對觀察者的add,get,register等方法,經過這一系列的方法向ApplicationEventMulticaster類中維護listener集合Set.改Set存儲了該發佈者全部的Listener,因此ApplicationContext容器會將注入到Spring中的Listener註冊到ApplicationEventMulticaster

image.png

  • 事件經過AbstractApplicationContext#publishEvent方法委託給AbstractApplicationEventMulticaster進行事件發佈,其中ApplicationEventMulticaster會先嚐試從ConfigurableListableBeanFactroy中加載配置文件的類,若是不存在就會默認new一個SimpleApplicationEventMulticaster.

image.png

  • 具體的事件發佈會在AbstractApplicaitonEventMulticaster#multicastEvent中實現,實現流程爲:先根據event獲取Listener集合,在線程池不爲空的狀況下,異步發佈特定類型的事件,不然同步發佈.在#invokeListener方法中最後調用Listener#onApplicationEvnet方法實現了事件的發佈.

image.png


7.總結

  • 以上就是關於Spring事件監聽機制的分析,其本質上是觀察者模式的實現.經過事件監聽機制可以將咱們代碼邏輯進行解耦,提升代碼的拓展性,實現開閉原則.
相關文章
相關標籤/搜索