轉自(http://samyulong.javaeye.com/blog/217092)
一、什么是攔截器?
提到攔截器,使我不得不 想起武俠劇中劫匪們常說的一句話:“此山是我開,此樹是我栽,要打此路過,留下買路財!”。難不成程序中也有“打劫”的,說的沒錯,攔截器就是個打劫的。 在現實生活中,劫匪劫的大都是錢財,當然也有別的什么,那么程序中的“劫匪”劫的又是什么呢?或者說程序中為什么需要它?在我們的日常編程中少不了寫一些 重復的代碼,例如在一個地方中寫了一段代碼,后來發現這段代碼在其它地方中同樣需要,在傳統的編程中我們一定會采取復制、粘貼的辦法。如果這段代碼只在這 一兩個處需要,我們采取這種辦法,還說的過去,但是如果系統對這段代碼過于依賴,也就是這段代碼在系統中出現的過多,如果那一天我們發現這段代碼中在某些 地方還需要完善,我們是不是要著個修改它們呢?我估計沒有人會這么做,它嚴重違反了軟件開發中一條非常重要的DRY規則,不寫重復代碼。說了這么多你一定知道我們為什么需要在程序中弄一個“劫匪”了吧。這個“劫匪”就是并不是劫取什么東西,只是為了在某個程序執行前后,動態的增加一些功能(以前所寫通用代碼塊)或進行一些檢查工作。那么這個攔截器到底是怎么實現的呢?實際上它是用Java中的動態代理來實現的。
二、攔截器在Struts2中的應用
對于Struts2框架而言,正是大量的內置攔截器完成了大部分操作。像params攔截器將http請求中參數解析出來賦值給Action中對應的屬性。Servlet-config攔截器負責把請求中HttpServletRequest實例和HttpServletResponse實例傳遞給Action……struts2內置的攔截器有很多,在此我就不一一列舉了
那么怎么在struts2中定義自己的攔截器呢?
很簡單Struts2為我們提供了一個Interceptor接口,該接口源代碼如下:
publicinterface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation) throws Exception;
}
1) init():在攔截器執行之前調用,主要用于初始化系統資源。
2) destroty():與init()對應,用于攔截器執行之后銷毀資源。
3) intercept():攔截器的核心方法,實現具體的攔截操作。與action一樣,該方法也返回一個字符串作為邏輯視圖。如果攔截器成功調用了action,則返回一個真正的,也就是該action中execute()方法返回的邏輯視圖,反之,則返回一個自定義的邏輯視圖。
通常我們使用攔截器并不需要申請資源,為此Struts2還為我們提供了一個AbstractInterceptor類,該類的init()和destroy()都是空實現。我們開發自己的攔截器只需要繼承這個類就行了。
下面創建一個判斷用戶是否登錄的攔截器。代碼如下:
import java.util.Map;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
@SuppressWarnings("serial")
public class CheckLoginInterceptor extends AbstractInterceptor {
@SuppressWarnings("unchecked")
public String intercept(ActionInvocation actionInvocation) throws Exception {
System.out.println("begin check login interceptor!");
// 檢查Session中是否存在user
Map session = actionInvocation.getInvocationContext().getSession();
String username = (String) session.get("user");
if (username != null && username.length() > 0) {
// 存在的情況下進行后續操作。
System.out.println("already login!");
return actionInvocation.invoke();
} else {
// 否則終止后續操作,返回LOGIN
System.out.println("no login, forward login page!");
return Action.LOGIN;
}
}
}
創建好攔截器后,還不能使用,還需要我們在struts.xml
中配置一下。
下面看一下怎么配置攔截器。
<interceptors>
<interceptor name="checkLogin" class="com.myblog.interceptor.CheckLoginInterceptor" />
</interceptors>
這個定義好的攔截器在Action中怎么使用呢?使用方法很簡單,如下:
<action name=" " class=" " >
<result> </result>
<interceptor-ref name="checkLogin" />
</action>
一旦我們為某個action引用了自定義的攔截器,struts2默認的攔截器就不會再起作用,因此還需要引用默認攔截器。
<action name=" " class=" " >
<result> </result>
<interceptor-ref name="checkLogin" />
<interceptor-ref name="defaultStack" />
</action>
但是我們這么做似乎也不太方便,因為如果攔截器checkLogin需要被多個action引用的話,每一個都要配置一遍太麻煩了。我們可以把它定義成默認的攔截器。
<interceptors>
<interceptor name="checkLogin" class="com.myblog.interceptor.CheckLoginInterceptor" />
<!—-定義一個攔截器棧-->
<interceptor-stack name="mydefault">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="checkLogin" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="mydefault" />
另外,struts2還為我們提供了一個方法過濾的攔截器MethodFilterInterceptor類,該類繼承AbstractInterceptor類,重寫了intercept(ActionInvocation invocation)并提供了一個新的方法doInterceptor(ActionInvocation invocation)抽象方法。該類的使用方法很簡單,就不舉例了。這個攔截器與以往的攔截器配置有所不同。那就是可以指定哪些方法需要被攔截,那些不需要。通常在引用該攔截器時指定。
<interceptor-ref name=" ">
<param name="exculdeMethods"></param>
<param name="includeMethods"></param>
</interceptor-ref>
exculdeMethods:是不被攔截的方法,如果有多個以逗號分隔。
includeMethods:需要被攔截的方法,如果有多個以逗號分隔。
下面我來實驗下。我們寫個攔截器棧
<interceptors>
<interceptor name="authorize" class="com.struts2.interceptor.AuthorizeInterceptor" />
<interceptor-stack name="appStack">
<!-- 你自定義的 -->
<interceptor-ref name="authorize"/>
<!-- 系統內置的攔截器棧 -->
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<action name="forward" class="com.struts2.RequestForward">
<interceptor-ref name="appStack"/>
<result name="index">index.jsp</result>
<result name="NOT_FOUND">not_found.jsp</result>
</action>
恩,還是有點很迷茫的位置,比說的攔截器的調用順序是根據xml里面的順序來的嗎?還有可以指定只在action之前或者只在action之后調用嗎?說實話我也搞不清楚,在運用的過程中,大家慢慢在來體會
攔截器幾乎完成了Struts2框架70%的工作,包括解析請求參數、將請求參數賦值給Action屬性、執行數據校驗、文件上傳……,Struts2設計的靈巧性,更大程度地得益于攔截器設計,當需要擴展Struts2功能時,只需要提供對應攔截器,并將它配置在Struts2容器中即可;如果不需要該功能時,也只需要取消該攔截器的配置即可。這種可插拔式的設計,正是軟件設計領域一直孜孜以求的目標。
實際上,Struts2的精髓就在于攔截器,掌握了Struts2的攔截器機制,你就可以說精通了Struts2。
從某個角度來看,我們可以把Struts2框架理解成一個空殼,而這些攔截器像一個一個抽屜,隨時可以
插進入,也可以拔出來——這是軟件產品一直追求的目標。
如果你喜歡,你可以把Struts2的全部插件拔出,那么Struts2就成了一個空容器——
而這種空,正是 Struts2的魅力,你可以把任何自己想要的東西填入進去,甚至包括自己完全實現這個框架。
另一方面,因為Struts2的插件機制,Struts2提供了無限擴展的可能性,你可以把自己想要的任何
東西做成插件,然后填入Struts2——這樣的結果是:一個企業,一個團隊,可以把自己業務相關的東西
做成插件,隨時隨地地復用。
也就是說:如果你想要,你可以把Struts2改造成屬于自己的框架。
當然,Struts2也內建了大量的攔截器,這些攔截器以name-class對的形式配置在struts-default. xml文件中,其中name是攔截器的名字,就是以后使用該攔截器的唯一標識;class則指定了該攔截器的實現類,如果我們定義的package繼承了Struts2的默認struts-default包,則可以自由使用下面定義的攔截器,否則必須自己定義這些攔截器。
下面是Struts2內建攔截器的簡要介紹:
alias:實現在不同請求中相似參數別名的轉換。
autowiring:這是個自動裝配的攔截器,主要用于當Struts2和Spring整合時,Struts2可以使用自動裝配的方式來訪問Spring容器中的Bean。
chain:構建一個Action鏈,使當前Action可以訪問前一個Action的屬性,一般和<result type="chain" .../>一起使用。
conversionError:這是一個負責處理類型轉換錯誤的攔截器,它負責將類型轉換錯誤從ActionContext中取出,并轉換成Action的FieldError錯誤。
createSession:該攔截器負責創建一個HttpSession對象,主要用于那些需要有HttpSession對象才能正常工作的攔截器中。
debugging:當使用Struts2的開發模式時,這個攔截器會提供更多的調試信息。
execAndWait:后臺執行Action,負責將等待畫面發送給用戶。
exception:這個攔截器負責處理異常,它將異常映射為結果。
fileUpload:這個攔截器主要用于文件上傳,它負責解析表單中文件域的內容。
i18n:這是支持國際化的攔截器,它負責把所選的語言、區域放入用戶Session中。
logger:這是一個負責日志記錄的攔截器,主要是輸出Action的名字。
model-driven:這是一個用于模型驅動的攔截器,當某個Action類實現了ModelDriven接口時,它負責把getModel()方法的結果堆入ValueStack中。
scoped-model-driven:如果一個Action實現了一個ScopedModelDriven接口,該攔截器負責從指定生存范圍中找出指定的Modol,并將通過setModel方法將該Model傳給Action實例。
params:這是最基本的一個攔截器,它負責解析HTTP請求中的參數,并將參數值設置成Action對應的屬性值。
prepare:如果action實現了Preparable接口,將會調用該攔截器的prepare()方法。
static-params:這個攔截器負責將xml中<action>標簽下<param>標簽中的參數傳入action。
scope:這是范圍轉換攔截器,它可以將Action狀態信息保存到HttpSession范圍,或者保存到ServletContext范圍內。
servlet-config:如果某個Action需要直接訪問Servlet API,就是通過這個攔截器實現的。
注意:盡量避免在Action中直接訪問Servlet API,這樣會導致Action與Servlet的高耦合。
roles:這是一個JAAS(Java Authentication and Authorization Service,Java授權和認證服務)攔截器,只有當瀏覽者取得合適的授權后,才可以調用被該攔截器攔截的Action。
timer:這個攔截器負責輸出Action的執行時間,這個攔截器在分析該Action的性能瓶頸時比較有用。
token:這個攔截器主要用于阻止重復提交,它檢查傳到Action中的token,從而防止多次提交。
token-session:這個攔截器的作用與前一個基本類似,只是它把token保存在HttpSession中。
validation:通過執行在xxxAction-validation.xml中定義的校驗器,從而完成數據校驗。
workflow:這個攔截器負責調用Action類中的validate方法,如果校驗失敗,則返回input的邏輯視圖。
大部分時候,開發者無需手動控制這些攔截器,因為struts-default.xml文件中已經配置了這些攔截器,只要我們定義的包繼承了系統的struts-default包,就可以直接使用這些攔截器。
當然,Struts2的攔截器機制并不是來自于Struts1,而是來自于WebWork。