博客 / 詳情

返回

幾句話熟悉Laravel/Symfony 事件系統

我們知道,laravel/symfony 框架是由一堆堆 components 組件粘合在一起的。其中會有一個 event component 組件,比較特殊,它像一箇中介,是框架層全局 component,專門負責不同component間相互通信傳數據的。

説它是全局的,意思是,整個請求生命週期內,event 對象是單例的,對象不能新建實例,每次從容器中獲取標識為 'event' 的 event 對象還是最開始的 event 對象,為啥要這樣搞?很簡單啊,如果不是單例的,那第一個 event component 裏註冊了 10 個事件 event,在某個 component 裏想要觸發 event 對象裏的一個發郵件事件 'mail',但如果此時從容器中拿到的 event component 不是原來的 event component,那就找不到那個 mail 事件對應的處理器 handler 了。可以看看 laravel 的 EventServiceProvider 在往容器中註冊 'event' 對象時是使用的 singleton() 單例註冊的,這樣保證 event 對象永遠是那一個。

説它負責跨組件通信,意思是比如對於 database 下的一個 model 對象如 account model,當邏輯處理到 save 完一個 account model 到數據庫時,如果需要再增加幾個邏輯,如發個郵件 mail,數據入數據庫前需要驗證 validation,給這個 account 寫個日誌 log 等等,這幾個邏輯需要 Mail Component/Validation Component/Log Component 裏面的對象去處理,難道我要在 $account->save() 的後面在寫一大坨這些邏輯?那代碼豈不亂七八糟。何況我想在 save 前和 save 後再搞一些邏輯,那不完蛋了。有沒有這樣一種實踐方式讓我 save 前一兩行代碼,save 後一兩行代碼,把這些邏輯解耦為各個小邏輯呢,這樣代碼豈不更清爽。

(1) 為啥需要事件系統?

代碼層面,為了代碼解耦,把在一處的一大坨邏輯解耦成多處細分代碼;性能考慮,事件的隊列功能模擬了異步處理,把耗時的任務放在隊列裏,讓隊列一個個去慢慢處理這些任務。

(2) 事件架構是如何構成的?

事件架構其實很簡單,它是一個框架層的全局 component: event component,通過上面的描述知道它應該具備幾個功能: 事件註冊功能事件觸發功能,再加一個高級功能把事件放在隊列裏異步處理,如 laravel 裏事件註冊功能\\Illuminate\\Events\\Dispatcher::listen(),事件觸發功能\\Illuminate\\Events\\Dispatcher::dispatch()

一個事件系統就這麼簡單。

(3) 一個事件 event 可以有多個處理器 handler/listener,一個 handler/listener 可以監聽多個事件,這個應該如何讓 event component 支持呢?

要求多個 handler/listener (一個 callable 或是一個 class name)監聽一個事件 'event',很簡單,那就多次註冊\\Illuminate\\Events\\Dispatcher::listen($event_name, $handler),$event_name 一樣,$handler 不一樣而已,會按照註冊順序執行 $handler,當然 symfony 支持在第三個參數就是 priority,設置 $handler 執行順序。
要求一個 handler/listener 可以監聽多個 event,很簡單,把 handler 做成一個類 class,然後裏面做個 $events 數組屬性設置要監聽哪些 events 就行,laravel/symfony 都叫這個 handler 為 Event Subscribers,僅此而已。

不想去畫圖來詳細説明了,根據上面幾句話,再去從架構層面去看 laravel event / symfony event 的事件系統文章,就很簡單了,建議仔細閲讀下官網這兩篇文章。其他細節都是為了上面這些設計目的服務的。

説了這麼多,一句話概括:事件系統就像是框架層的全局數據庫,具有存儲、註冊和觸發事件功能,解耦代碼,實現跨組件通信。。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.