2009年3月15日 星期日

[Struts2] Introduce Interceptor

之前我們在 How Struts2 works 中有稍微的談到 Interceptor。在 Struts2 中 Interceptor 算是與 Struts1 不同處的最大突破!因為 Interceptor 幫助我們可以很容易的 reuse components 的功能。而且 Interceptor 在 Struts2 framework 中扮演著很重要的關鍵角色,因為 Interceptor 的導入,讓 Struts2 framework 更達到 Separation of concerns。(也就是專門的 component 就做自己專門的工作,無須與其他 components 有太過複雜的互動等)

Separation of concerns 讓系統可以更清楚的劃分各個區塊的工作,在 Struts2 framework 中主要 focus 的 concern 就是 MVC,所以 Separation of concerns 讓 Struts2 的 MVC 架構更乾淨明瞭!而且從架構的觀點來說,interceptor 讓我們可以更專注的在開發我們的 web application,讓一些每個部份都需要用到的功能(例如:Logging 等)放在 interceptor 中給大家共用,而我們的 business logic 並不會受到任何的影響。換句話說,當我們在開發我們的 Actions 時,我們不用自己在 Action 中加入 Logging 的程式碼,透過 interceptor 幫我們做 logging 的工作,並且所有的 Actions 都可以享有這樣的功能!這種就是 cross-cutting concern

How Struts2 works 中我們有提到:Interceptor stack 是在整個流程中第一個被執行,也是最後一個被執行的!這就表示說 interceptor stack 中蒐集了很多的 interceptors,而這些 interceptors 都可以在執行我們的 Action 之前做一些 preprocessing 與 postprocessing 的工作!一個最直觀的 preprocessing 工作就是 data transfer,還記得使用者在 JSP 頁面中可以輸入資料,而這些資料最後都會被 mapping 到 Action 的 properties,這樣的工作就是交由 param interceptor 幫我們完成的!而這工作是必須在我們的 Action 執行前就要先被系統完成的前置工作,因為我們的 Action 在執行 execute() method 時,通常都需要使用者輸入的資料。而 postprocessing 就以 Logging 為例,我們可能希望系統執行過哪些 Actions 的紀錄要做 Log 以方便日後追查用等等,所以就需要在 Action 執行完之後進行 Logging 的作業。

圖一 ActionInvocation

圖一中表示,當 Struts2 framework 呼叫使用者所 request 的 Action 前,會先呼叫該 Action 所屬的 configuration file(.xml) 中 interceptor stack,這裡會定義一些再呼叫 Action 之前的動作,這些動作與 Action 是沒有直接關聯,但是卻是讓撰寫 Action 的 programmer 可以不用煩惱 detail 的動作。從 interceptor stack 為 stack 就知道,這樣的呼叫過程式 Layered process 呼叫方式,而 stack 的精神就是 FILO(First-In-Last-Out),所以在 interceptor stack 中的第一個 interceptor 會被第一個呼叫,也會是最後一個呼叫。如圖一中的第一個 interceptor 為 exception interceptor,在 Action 執行會是第一個被執行的 interceptor,當 Action 執行完後,Struts2 安排好顯示給使用者的Result 畫面,接著才會在回到 interceptor stack 中依照剛剛相反地順序執行 interceptors,所以這時候 exception interceptor 就會變成整個流程中最後一個被執行的 interceptor。如果我們的 interceptor 在 stack 中是最後一個的話,我們不必直接的呼叫 Action 的 execute() method,因為 Struts2 framework 會產生一個 ActionInvocation object 物件負責協調各個 interceptors 之間以及與 Action 之間的互動,甚至我們無須知道我們的 interceptor 是處在 stack 中的哪一個位置以及我們前後的 interceptor 又是哪些,我們一樣可以很簡單的去完成 interceptor 需要執行的功能!

這種 Layered 結構讓我們的 web application 保持簡單乾淨,甚至提昇了 interceptor 的 readabililty, testing 與 flexibility 特性。

對於 readability 來說,因為每一個 interceptor 可以是獨立的,也可以與其他 interceptors 合作,但是對於我們的程式來說並不用考慮這樣的問題,我們只需要專心的撰寫我們 interceptor 所要提供的功能就好,剩下的是就交給 Struts2 framework 來幫我們完成!這樣就可以增加整個 interceptor 程式碼的可讀性,而且與 framework 之間的 coupling 降低,而 cohesion 提昇。

同樣的,testing 也會變得相對的容易的多,因為我們不必知道太多其他 detail 而可以很容易的去測試我們的程式碼。

最後就是 flexibility,就如同之前所說的,因為我們的 interceptor 不必知道其他 interceptors 之間的資訊,但是彼此間又可以合作,所以我們就可以讓我們的 interceptor 既可以獨立運作又可以互相合作,並且我們的 interceptor 可以根據 configuration file 的設定來改變 interceptor 之間的位置來達到不同的效果,這樣就是增加了 flexibility。

對於初次接觸 Struts2 framework 的使用者來說,對於 interceptor 的設定並不是哪麼的容易,因為有些 interceptors 是 Struts2 原先就提供給 programmer 很方便的工具,如果我們忘記將這些 interceptors 加入 stack 中,那我們的 web application 就會跑不起來。雖然我們之前有說過我們可已將 interceptors 之間的位製作調整,但是有些 interceptors 要先被放置在 stack 的上面(第一次先被執行),之後的 interceptors 才能夠發揮其作用,所以彼此之間是有那麼一點關聯性存在。幸好 Struts2 framework 提供了 defaultStack 讓初學的人對於設定 interceptor stack 不用在煩心,而且對於初學者來說,defaultStack 中所安排的 interceptors 幾乎都可以滿足需求!那你可能會問,我們在初啼試聲 HelloStruts2 中好像沒有設定我們的 defaultStack 耶!的確,我們並沒有很明確的設定我們的 defaultStack,不過當我們對 <package> tag 設定 extends attribute 時就已經間接的設定我們的 defaultStack!因為 defaultStack 是被設定在 struts-default 的 package 中,當我們 package extends struts-default 就繼承了 struts-default 中的所有設定!所以對於初學的 programmer 來說,我們就使用 defaultStack 作為我們的 interceptor stack!

2 則留言:

WEI 提到...

請教您一個問題: 當使用者在網頁上輸入xxx.jsp時(此xxx.jsp有用到struts2 tag library.如property tag. 那struts2 framework 會執行到default interceptor嗎? 謝謝!

Silver Su 提到...

似乎不會喔!因為沒有通過 action 的話不會經過 Struts2 的 filter,所以應該不會進入 Struts2 framework 中!也就不過經過 interceptor stack 了!