在 Struts2 framework 中已經提供了 struts-default.xml 幫助我們設定了一些內建的 interceptors 以及 interceptor stack,這讓我們減輕了不少的工作,不過我們在開發大型的系統時,這些預設的設定檔可能不太適用,所以我們還是要知道怎麼樣去自己設定 interceptors。
宣告與設定 interceptors
基本上來說,interceptor 的設定包含了兩個部份:interceptor 的宣告與 interceptor stack 的設定。我們就參考 struts-default.xml 中的 interceptor:
<package name="struts-default">
...
<interceptors>
<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
<interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
<interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
<interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
<interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
<interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
<interceptor name="externalRef" class="com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor"/>
<interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
<interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
<interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
<interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
<interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
<interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
<interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
<interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
<interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
<interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
<interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
<interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
<interceptor name="sessionAutowiring" class="org.apache.struts2.spring.interceptor.SessionContextAutowiringInterceptor"/>
<interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
<interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
<interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
<interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
<interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
<interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
<interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" />
<interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" />
<interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" />
<interceptor name="jsonValidation" class="org.apache.struts2.interceptor.validation.JSONValidationInterceptor" />
<interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" />
...
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="profiling"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
...
</interceptors>
<default-interceptor-ref name="defaultStack"/>
...
</package>
由上面的設定中我們可以看到,整個 interceptors 是宣告在 interceptor tag 之中,而且如同其他 framework components 一樣,這些設定都是屬於 package tag 之下!
在 interceptors tag 之中包含了兩組 tags:interceptor 與 interceptor-stack,我們可以從 tag 的名稱很容易瞭解其功能。
首先,我們透過 interceptor tag 來宣告我們擁有的 interceptors,當然,interceptors 要先被宣告才能使用,這就跟 programming language 一樣!而且宣告 interceptor 是很簡單的,我們透過 name attribute 給予該 interceptor 一個宣告的名稱,就像程式中宣告變數的名稱一樣;再來我們就是透過 class attribute 告知 framework,該 interceptor 所屬的 class 位置,這個位置一定要包含完整的 package name 與 class name(這裡說得是 class 位置,而不是檔案位置喔!所以最後面不需要加上 .java)!
宣告了 interceptors 之後,我們就可以設定我們的 interceptor stack,透過 interceptor-stack tag 來宣告一組 stack。這裡由於原始的 struts-default.xml 中的設定太過於龐大,所以我們只有顯示 defaultStack 的 interceptor stack。每一組 interceptor stack 我們都要給予一個唯一的 name attribute,而一個 interceptor stack 中都包含了一系列有順序性的 interceptor-ref tag,這個 tag 也是很容易瞭解的,interceptor-ref tag 就是用來參考(reference) 我們所宣告的 interceptors,所以我們在 interceptor-ref tag 中設定 name attribute 來參考到我們所宣告的 interceptors。
請注意剛才我所說得"有順序性",因為我們所宣告在 interceptor-stack tag 中的 interceptor ref 順序是在將來系統執行時由上而下的來執行!所以如果有某些順序被更動,可能會造成整個 interceptor stack 的不同。如果我們將 param interceptor 移到 workflow interceptor 之後,那我們就無法在 workflow interceptor 中使用param tag 來給予設定參數,因為 framework 對於 param tag 的執行是透過 param interceptor 來進行的,所以 param interceptor 要先被呼叫後,才能在之後的 interceptors 使用 param tag 進行參數設定!
當我們宣告完成 interceptors 以及設定好 interceptor stack 後,我們就要設定 framework 中預設使用的 interceptor stack 了!這個可以透過 default-interceptor-ref tag 來設定,我們透過給予 default-interceptor-ref tag 中的 name attribute 來告知 framework,我們要使用的 default interceptor stack!
針對每個 Action 設定特定的 interceptors
許多時候我們的 Action 都是繼承自其所屬 package 中對於 interceptors 的設定,不過也有時候我們可能會需要將某些 interceptors 用在某個 Action 呼叫之前,在 Struts2 framework 中提供了一個很方便的設定方式,讓我們可以為某一個 Action 給予特定的 interceptors,讓我們看看下面的設定:
<action name="MyAction" class="silver8250.test.MyAction">
<interceptor-ref name="timer" />
<interceptor-ref name="logger" />
<result>/WEB-INF/pages/finish.jsp</result>
</action>
這個設定中告知 Struts2 framework,我們有一個 MyAction 的 Action,要使用 timer 與 logger interceptor,而且 interceptor 呼叫的順序是依照我們的排列,不過我們的 MyAction 是不會有其他的 interceptors 了!這點要注意,如果我們採用 Action 中自己定義的 interceptors,那我們就會失去原先在設定檔中設定的 interceptor stack 了!不過為了讓我們可以很方便的 reuse 原先我們設定的 interceptor stack,我們可以直接在 interceptor-ref tag 中設定某個已經存在的 interceptor stack,如下:
這種設定方式讓我們可以很方便的 reuse 某個 interceptor stack,當然,我們可以安排不一樣的順序!
<action name="MyAction" class="silver8250.test.MyAction">
<interceptor-ref name="timer" />
<interceptor-ref name="logger" />
<interceptor-ref name="defaultStack" />
<result>/WEB-INF/pages/finish.jsp</result>
</action>
Action 中 Overriding interceptor 的參數
上面的方法是為某個 Action 設定不同的 interceptor stack,現在我們則是要修改 Action 的 interceptor stack 中的某個參數,先看看下面的設定:
在這裡的 MyAction 我們一樣給予一個我們自己設定的 interceptor stack,只不過是 reuse defaultStack,這樣的方式讓我們可以調整我們 reuse 的 interceptor stack,就如同上面所設定的:透過 param tag 告知 framework,目前 MyAction 是要使用 defaultStack 中定義的 interceptor stack,不過對於 workflow interceptor,我們要設定 excludeMethods 改為 someMethod 來取代原先設定的參數!
這種方式讓我們可以為某個 Action 調整特定的 interceptor stack,當然,我們也可以將這種方法結合上面的方法來完成更多不同的 interceptor stack。
<action name="MyAction" class="silver8250.test.MyAction">
<interceptor-ref name="defaultStack">
<param name="workflow.excludeMethods">someMethod</param>
</interceptor-ref>
<result>/WEB-INF/pages/finish.jsp</result>
</action>
在這篇中我們深入了描述了 interceptor 的設定與宣告,我們可以為一整個 package 中的 Actions 設定共用的 interceptor stack,也可以特定位某個 Action 設定特別的 interceptor stack,也可以 reuse 某個 interceptor stack 並且調整不同的設定參數,甚至我們可以結合這兩種方式來提供更好用的 interceptor stack!