回覆列表
  • 1 # Java實用技術

    關於責任鏈模式,其有兩種形式,一種是透過外部呼叫的方式對鏈的各個節點呼叫進行控制,從而進行鏈的各個節點之間的切換;另一種是鏈的每個節點自由控制是否繼續往下傳遞鏈的進度,這種比較典型的使用方式就是Netty中的責任鏈模式。本文主要講解我們如何在Spring中使用這兩種責任鏈模式。

    1. 外部控制模式

    對於外部控制的方式,這種方式比較簡單,鏈的每個節點只需要專注於各自的邏輯即可,而當前節點呼叫完成之後是否繼續呼叫下一個節點,這個則由外部控制邏輯進行。這裡我們以一個過濾器的實現邏輯為例進行講解,在平常工作中,我們經常需要根據一系列的條件對某個東西進行過濾,比如任務服務的設計,在執行某個任務時,其需要經過諸如時效性檢驗,風控攔截,任務完成次數等過濾條件的檢驗之後才能判斷當前任務是否能夠執行,只有在所有的過濾條件都完成之後,我們才能執行該任務。那麼這裡我們就可以抽象出一個介面,其設計如下:

    這裡的方法只有一個引數,主要就是控制當前task是否需要被過濾掉,其有一個boolean型別的返回值,透過該返回值以告知外部控制邏輯是否需要將該task過濾掉。對於該介面的子類,我們只需要將其宣告為Spring所管理的一個bean即可:

    上面我們模擬聲明瞭三個的子類,用於設計一系列的控制當前task是否需要被過濾的邏輯,結構上的邏輯其實比較簡單,主要就是需要將其宣告為Spring所管理的一個bean。下面是我們的控制邏輯:

    在上述的控制邏輯中,對於過濾器的獲取,只需要透過Spring的自動注入即可,這裡注入的是一個,也就是說,如果我們有新的例項需要參與責任鏈的過濾,只需要將其宣告為一個Spring容器所管理的bean即可。

    這種責任鏈設計方式的優點在於鏈的控制比較簡單,只需要實現一個統一的介面即可,其基本上能夠滿足大部分的邏輯控制,但是對於某些需要動態調整鏈的需求其就無能為力了。比如在執行到某個節點之後需要動態的判斷是否執行下一個節點,或者說要執行某些分叉的節點等等。這個時候我們就需要將鏈節點的傳遞工作交由各個節點進行。

    2. 節點控制模式

    對於節點控制呼叫的方式,其主要有三個控制點:Handler,HandlerContext和Pipeline。Handler中是用於編寫具體的業務程式碼的;HandlerContext則主要是用於對Handler進行包裹,並且用於控制進行下一個節點的呼叫的;Pipeline則主要是用於控制整體的流程呼叫的,比如對於任務的執行,其有任務的查詢,任務的過濾和執行任務等等流程,這些流程整體的邏輯控制就是由Pipeline來控制的,在每個流程中又包含了一系列的子流程,這些子流程則是由一個個的HandlerContext和Handler進行梳理的。這種責任鏈的控制方式整體邏輯如下圖所示:

    從圖中可以看出,我們將整個流程透過物件進行了抽象,這裡主要分為了三個步驟:查詢task,過濾task和執行task。在每個步驟中,我們都使用了一系列的鏈式呼叫。圖中需要注意的是,在每次呼叫鏈的下一個節點的時候,我們都是透過具體的Handler進行的,也就是說是否進行鏈的下一個節點的呼叫,我們是透過業務實現方來進行動態控制的。

    關於該模式的設計,我們首先需要強調的就是介面的設計,其設計如下所示:

    這裡的介面主要是對具體的業務邏輯的一個抽象,對於該主要有如下幾點需要說明:

    在中,我們需要說明如下幾點:

    之前介面預設實現的方法,在這裡都委託給了對應的方法進行呼叫,而且我們需要注意到,在傳遞給方法的引數裡,傳入的物件都是透過方法獲取到的。也就是說我們在中呼叫方法時,都是在呼叫當前handler的下一個handler對應層級的方法,透過這種方式我們就實現了鏈的往下傳遞。在上一點中我們說到,在某個中如果想讓鏈往下傳遞,只需要呼叫方法即可,也就是說,如果我們在某個中,如果根據業務,當前層級已經呼叫完成,而無需呼叫後續的,那麼我們就不需要呼叫方法即可;在中,我們也實現了方法,該方法的主要作用是供給外部的進行呼叫的,以開啟每個層級的鏈;在每個方法中,我們都使用try…catch將當前層級的呼叫丟擲的異常給捕獲了,然後呼叫方法處理該異常,這也就是我們前面說的,如果想處理當前中的異常,只需要實現該中的方法即可,異常捕獲流程就是在這裡的中進行處理的;在的宣告處,我們需要注意到,其使用了和註解進行標註了,這說明我們的是由Spring所管理的一個bean,並且由於我們每一個實際上都由一個維護著,所以這裡必須宣告為型別。透過這種方式,我們的也就具備了諸如Spring相關的bean的功能,也就能夠根據業務需求進行一些額外的處理了;

    前面我們講解了和的具體實現,以及實現的過程中需要注意的問題,下面我們就來看一下進行流程控制的是如何實現的,如下是介面的定義:

    這裡 主要是定義了一個介面,該介面定義了一系列的層級呼叫,是每個層級的入口方法。如下是該介面的一個實現類:

    關於的實現,主要有如下幾點需要說明:

    使用和註解進行了標註,前一個註解用於將其宣告為一個Spring容器所管理的bean,而後一個註解則用於表徵是一個多例型別的,很明顯,這裡的是有狀態的。這裡需要進行說明的是,"有狀態"主要是因為我們可能會根據業務情況動態的調整個鏈的節點情況,而且這裡的和物件都是與具體的業務相關的,因而必須宣告為型別;上面的示例中,物件是透過構造物件的時候傳進來的,而物件則是在的流轉過程中生成的,這裡比如透過完成鏈的呼叫之後,就需要透過外部請求得到一個物件,從而進行整個的後續處理;

    這裡我們已經實現了,和,知道這些bean都是被Spring所管理的bean,那麼我們接下來的問題主要在於如何進行整個鏈的組裝。這裡的組裝方式比較簡單,其主要需要解決兩個問題:

    對於後續寫業務程式碼的人而言,其只需要實現一個介面即可,而無需處理與鏈相關的所有邏輯,因而我們需要獲取到所有實現了介面的bean;將實現了介面的bean透過進行封裝,然後將其新增到中。

    這裡的第一個問題比較好處理,因為透過ApplicationContext就可以獲取實現了某個介面的所有bean,而第二個問題我們可以透過宣告一個實現了BeanPostProcessor介面的類來實現。如下是其實現程式碼:

    這裡我們整個鏈的維護工作就已經完成,可以看到,現在基本上已經實現了前面圖中整個鏈式流程的控制。這裡需要說明的一點是,上面的方法的執行是在方法之後執行的,也就是說這裡在執行時,整個是已經初始化完成了的。下面我們來看一下外部客戶端如何進行整個鏈是流程的控制:

    這裡我們模擬了一個客戶端的呼叫,首先建立了一個物件,然後依次呼叫其各個層級的方法,並且這裡我們使用try…finally結構來保證方法一定會執行。如此我們就完成了整個責任鏈模式的構造。這裡我們使用前面用到的時效性過濾的filter來作為示例來實現一個:

    關於這裡的具體業務我們需要說明的有如下幾點:

    該必須使用註解來將其宣告為Spring容器所管理的一個bean,這樣我們前面實現的才能將其動態的新增到整個中;在每個中,需要根據當前的業務需要來實現具體的層級方法,比如這裡是進行時效性檢驗,就是"任務過濾"這一層級的邏輯,因為時效性檢驗透過我們才能執行這個task,因而這裡需要實現的是方法,如果我們需要實現的是執行task的邏輯,那麼需要實現的就是方法;在實現完具體的業務邏輯之後,我們可以根據當前的業務需要看是否需要將當前層級的鏈繼續往下傳遞,也就是這裡的方法的呼叫,我們可以看前面方法就是會獲取當前節點的下一個節點,然後進行呼叫。如果根據業務需要,不需要將鏈往下傳遞,那麼就不需要呼叫;

    3. 小結

    如此,我們就透過兩種方式實現了責任鏈模式,而且我們實現的責任鏈模式都是符合"開-閉"原則的,也就是說後續我們要為鏈新增新的節點的時候,只需要根據規範實現相應的介面即可,而無需處理鏈的維護相關的工作。關於第二種實現方式,這裡我們並沒有實現鏈節點的順序控制功能,以及如何動態的新增或刪除鏈的節點,更有甚者,如果控制每個Handler是單例的還是多例的。當然,有了前面的框架,這些點實現起來也比較簡單,這裡權當起到一個拋磚引玉的作用,讀者朋友可根據自己的需要進行實現。

  • 中秋節和大豐收的關聯?
  • 高中生去義大利留學有什麼要求和優勢?