struts2的攔截器(Interceptor)是struts中比較復雜也比較核心的內(nèi)容,功能挺強大。不過對于這種很難很強大的東東,一般還是比較抵觸的:畢竟學起來比較艱深,學完了有沒有用又另當別論了——那為什么還要學呢? 如果不能解決實際問題,它吹得再牛,也不見得有多好。所以,攔截器到底做什么的呢?
攔截器的一個最廣泛的應(yīng)用是全局訪問權(quán)限控制。
在做網(wǎng)站的時候,一般來說都有全局的權(quán)限控制這一塊。如果在每個頁面的前頭加一段代碼的話,頁面少還問題不大,頁面多了維護起來就比較恐怖了。Struts中提供了一種全局性的解決方案:攔截器。 攔截器的底層實現(xiàn)機制還是AOP,其原理是……AOP的實現(xiàn)原理就不說了,攔截器的原理是在特定函數(shù)的執(zhí)行前后,插入自定義方法,具體到對Action攔截器中,就是每個(或者每個指定的)Action的Execute()或者其他指定方法執(zhí)行前后,插入自定義的方法。
也就是說,你可以在(除了登錄Action本身之外)所有Action邏輯發(fā)生之前,插入一段用戶驗證邏輯,從而將繞過登錄界面的用戶請求擋在核心業(yè)務(wù)邏輯之外;進一步的,可以將各種業(yè)務(wù)按照權(quán)限分級分類,使用不同的攔截器邏輯進行攔截,從而達到多級權(quán)限控制的效果。
上面說的只是攔截器的一部分功能,其實struts自己也預定義了許多攔截器,比如說。Action映射、參數(shù)解析、日志、國際化 、驗證……struts中許多非常核心的功能其實都是通過攔截器機制來實現(xiàn)的。如果有興趣了解,struts2-core-xxx.jar中struts-default.xml的<interceptors>節(jié)點可以作為起點,其中定義了struts內(nèi)建的所有攔截器。
下面簡單記錄實現(xiàn)步驟 1、定義攔截器。 Struts2規(guī)定用戶自定義攔截器必須實現(xiàn)com.opensymphony.xwork2.interceptor.Interceptor接口 。該接口聲明了3個方法,
其中,init和destroy方法會在程序開始和結(jié)束時各執(zhí)行一遍,不管使用了該攔截器與否,只要在struts.xml中聲明了該攔截器就會被執(zhí)行。 intercept方法就是攔截的主體了,每次攔截器生效時都會執(zhí)行其中的邏輯。
不過,struts中又提供了幾個抽象類來簡化這一步驟。 public abstract class AbstractInterceptor implements Interceptor; public abstract class MethodFilterInterceptor extends AbstractInterceptor; 都是模板方法實現(xiàn)的。 其中AbstractInterceptor提供了init()和destroy()的空實現(xiàn),使用時只需要覆蓋intercept()方法; 而MethodFilterInterceptor則提供了includeMethods和excludeMethods兩個屬性,用來過濾執(zhí)行該過濾器的action的方法??梢酝ㄟ^param來加入或者排除需要過濾的方法。
一般來說,攔截器的寫法都差不多??聪旅娴氖纠?br />
其中,if()邏輯內(nèi)執(zhí)行了從session中取用戶信息及驗證的邏輯,如果成功,則調(diào)用invocation.invoke()將邏輯交給Action,否則退出并返回一個全局的Login結(jié)果。
2.聲明攔截器 攔截器需要在struts.xml中聲明。在說明步驟之前,先介紹一下struts中攔截器的框架。
在struts中攔截器實際上分為攔截器和攔截器棧,攔截器棧可以包含一到多個攔截器或者攔截器棧。從上層看來,攔截器和攔截器棧實際上沒有什么區(qū)別(就像操作系統(tǒng)中的文件夾和文件)。struts在入口處遞歸的調(diào)用了<default-interceptor-ref>中定義的攔截器(棧)中的所有攔截器。
其實之前如果看過struts-default.xml的話,可以看到struts中內(nèi)建了許多的攔截器,事實上,即便我們在struts.xml中什么都不聲明,程序也會在后臺執(zhí)行缺省攔截器棧中定義的許多攔截器邏輯,比如說將頁面上的field映射到對應(yīng)Action的同名屬性中、自動執(zhí)行類型轉(zhuǎn)換、自動關(guān)聯(lián)驗證xml等等。這些攔截器會在每個沒有顯式聲明攔截器的Action執(zhí)行前后被執(zhí)行。
需要注意的是,正如上面一句所提到的,如果在Action中顯式聲明了一個攔截器,那么系統(tǒng)默認的攔截器將不會被調(diào)用。因此,如果直接將自定義的攔截器放入Action中的話,內(nèi)建的那些攔截器將會被忽略,這會導致錯誤。所以我們需要在struts.xml的<package>元素下覆蓋缺省攔截器。像下面這樣:
這樣就將自定義攔截器加上了struts缺省攔截器形成新的缺省攔截器。
因為全局定義了攔截器,雖然攔截器在通過攔截的情況下會返回特定Action的result,但有時候比如權(quán)限驗證失敗等情況下,自定義攔截器會返回自定義的結(jié)果,不屬于任何特定Action,所以我們也需要定義一個全局result用以響應(yīng)這個攔截器的返回值。 緊挨在<default-interceptor-ref>元素下面添加
注意到其result type為redirectAction重定向Action,這樣struts將以重定向方式處理該Action,并跳轉(zhuǎn)到global_error.jsp而不會顯示出最后一次執(zhí)行的Action名字。順便,Action的result缺省是以dispatcher也就是請求轉(zhuǎn)發(fā)的方式處理的。
同時,按照業(yè)務(wù)邏輯,有些特定Action是不能執(zhí)行自定義攔截器的。比如說,如果我們定義了一個全局的攔截器,它從session中取出用戶名和密碼進行驗證,驗證通過則繼續(xù),不通過則返回到login.jsp頁面,那么很顯然login.jsp頁面提交的那個Action本身是不能使用該攔截器的,否則就沒有地方可以將用戶信息放入session了。 這種情況下,就要借助之前提到的“特定Action中定義的攔截器會覆蓋全局設(shè)置”這個特性了。在需要屏蔽該攔截器的個別Action中顯式的聲明defaultStack攔截器(也就是struts內(nèi)建的攔截器棧),這樣,自定義攔截器在這個Action中就不會生效了。
小結(jié)一下: 寫了這么多,其實大部分都在講原理,如果理解了原理,這幾部做起來是否常容易的。所以重要的是掌握原理。 回憶一下,真正要做的只有三個地方: (1)寫一個攔截器類(實現(xiàn)接口也好,繼承抽象類也好) (2)修改struts.xml聲明新的缺省攔截器棧和全局result (3)對于特定Action,顯式聲明defaultStack攔截器以屏蔽自定義攔截器
另外談一點自己的看法: 攔截器可以說是struts當之無愧的核心,無論是struts的實現(xiàn)原理還是我們基于struts搭建的應(yīng)用擴展,攔截器都發(fā)揮了重要的作用。當然也有可能是我自己的理解不夠深入,但是有一點沒想明白,就是攔截器只能基于Action發(fā)揮效用,如果一些功能和Action無關(guān),那么攔截器沒法發(fā)生效用。不知道struts底層實現(xiàn)細節(jié)中是否只開放了基于Action的攔截器,其實理論上來說,在web.xml中設(shè)置了struts作為filter,并攔截所有請求,攔截器在從頁面發(fā)送至服務(wù)器被struts filter到的那一刻起(準確地說是“前一刻起”),攔截器就能發(fā)揮作用了。比如說現(xiàn)在這種過濾器只能在頁面提交到Action時起作用,但無法防止用戶直接訪問JSP。
posted on 2009-02-06 14:42 bacon 閱讀(4087) 評論(1) 編輯 收藏 所屬分類: J2EE
在struts.xml中修改一下action擴展名的配置,應(yīng)該就能使攔截器處理 .jsp的請求了: <constant name="struts.action.extension" value="action,jsp" />就是把 .jsp 請求也當作action攔截下來進行處理 回復 更多評論
Powered by: BlogJava Copyright © bacon