一、理解Struts2攔截器
1. Struts2攔截器是在訪問某個Action或Action的某個方法,字段之前或之后實施攔截,並且Struts2攔截器是可插拔的,攔截器是AOP的一種實現.
2. 攔截器棧(Interceptor Stack)。Struts2攔截器棧就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,Struts2攔截器鏈中的攔截器就會按其之前定義的順序被調用。
二、實現Struts2攔截器原理
Struts2攔截器的實現原理相對簡單,當請求struts2的action時,Struts 2會查找配置文件,並根據其配置實例化相對的 攔截器對象,然后串成一個列表,最后一個一個地調用列表中的攔截器
三、定義Struts2攔截器。
Struts2規定用戶自定義攔截器必須實現com.opensymphony.xwork2.interceptor.Interceptor接口。該接口聲明了3個方法,
| void init(); void destroy(); String intercept(ActionInvocation invocation) throws Exception; |
其中,init和destroy方法會在程序開始和結束時各執行一遍,不管使用了該攔截器與否,只要在struts.xml中聲明了該Struts2攔截器就會被執行。
intercept方法就是攔截的主體了,每次攔截器生效時都會執行其中的邏輯。
不過,struts中又提供了幾個抽象類來簡化這一步驟。
public abstract class AbstractInterceptor implements Interceptor; public abstract class MethodFilterInterceptor extends AbstractInterceptor; |
都是模板方法實現的。
其中AbstractInterceptor提供了init()和destroy()的空實現,使用時只需要覆蓋intercept()方法;
而MethodFilterInterceptor則提供了includeMethods和excludeMethods兩個屬性,用來過濾執行該過濾器的action的方法。可以通過param來加入或者排除需要過濾的方法。
一般來說,攔截器的寫法都差不多。看下面的示例:
package interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class MyInterceptor implements Interceptor {
public void destroy() {
// TODO Auto-generated method stub
}
public void init() {
// TODO Auto-generated method stub
}
public String intercept(ActionInvocation invocation) throws Exception {
System.out.println("Action執行前插入 代碼");
//執行目標方法 (調用下一個攔截器, 或執行Action)
final String res = invocation.invoke();
System.out.println("Action執行后插入 代碼");
return res;
}
} |
四、配置Struts2攔截器
Struts2攔截器需要在struts.xml中聲明,如下struts.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.objectFactory" value="spring" />
<package name="default" extends="struts-default"> <interceptors> <interceptor name="MyInterceptor" class="interceptor.MyInterceptor"></interceptor> <interceptor-stack name="myInterceptorStack"> <interceptor-ref name="MyInterceptor"/> <interceptor-ref name="defaultStack"/> </interceptor-stack> </interceptors> <action name="loginAction" class="loginAction"> <result name="fail">/index.jsp </result> <result name="success">/success.jsp</result> <interceptor-ref name="myInterceptorStack"></interceptor-ref> </action> </package> </struts> |
| 攔截器 |
名字 |
說明 |
| Alias Interceptor |
alias |
在不同請求之間將請求參數在不同名字件轉換,請求內容不變 |
| Chaining Interceptor |
chain |
讓前一個Action的屬性可以被后一個Action訪問,現在和chain類型的result()結合使用。 |
| Checkbox Interceptor |
checkbox |
添加了checkbox自動處理代碼,將沒有選中的checkbox的內容設定為false,而html默認情況下不提交沒有選中的checkbox。 |
| Cookies Interceptor |
cookies |
使用配置的name,value來是指cookies |
| Conversion Error Interceptor |
conversionError |
將錯誤從ActionContext中添加到Action的屬性字段中。 |
| Create Session Interceptor |
createSession |
自動的創建HttpSession,用來為需要使用到HttpSession的攔截器服務。 |
| Debugging Interceptor |
debugging |
提供不同的調試用的頁面來展現內部的數據狀況。 |
| Execute and Wait Interceptor |
execAndWait |
在后台執行Action,同時將用戶帶到一個中間的等待頁面。 |
| Exception Interceptor |
exception |
將異常定位到一個畫面 |
| File Upload Interceptor |
fileUpload |
提供文件上傳功能 |
| I18n Interceptor |
i18n |
記錄用戶選擇的locale |
| Logger Interceptor |
logger |
輸出Action的名字 |
| Message Store Interceptor |
store |
存儲或者訪問實現ValidationAware接口的Action類出現的消息,錯誤,字段錯誤等。 |
| Model Driven Interceptor |
model-driven |
如果一個類實現了ModelDriven,將getModel得到的結果放在Value Stack中。 |
| Scoped Model Driven |
scoped-model-driven |
如果一個Action實現了ScopedModelDriven,則這個攔截器會從相應的Scope中取出model調用Action的setModel方法將其放入Action內部。 |
| Parameters Interceptor |
params |
將請求中的參數設置到Action中去。 |
| Prepare Interceptor |
prepare |
如果Acton實現了Preparable,則該攔截器調用Action類的prepare方法。 |
| Scope Interceptor |
scope |
將Action狀態存入session和application的簡單方法。 |
| Servlet Config Interceptor |
servletConfig |
提供訪問HttpServletRequest和HttpServletResponse的方法,以Map的方式訪問。 |
| Static Parameters Interceptor |
staticParams |
從struts.xml文件中將中的中的內容設置到對應的Action中。 |
| Roles Interceptor |
roles |
確定用戶是否具有JAAS指定的Role,否則不予執行。 |
| Timer Interceptor |
timer |
輸出Action執行的時間 |
| Token Interceptor |
token |
通過Token來避免雙擊 |
| Token Session Interceptor |
tokenSession |
和Token Interceptor一樣,不過雙擊的時候把請求的數據存儲在Session中 |
| Validation Interceptor |
validation |
使用action-validation.xml文件中定義的內容校驗提交的數據。 |
| Workflow Interceptor |
workflow |
調用Action的validate方法,一旦有錯誤返回,重新定位到INPUT畫面 |
| Parameter Filter Interceptor |
N/A |
從參數列表中刪除不必要的參數 |
| Profiling Interceptor |
profiling |
通過參數激活profile |
===============================過濾器===========================================================
過濾器,是在java web中,你傳入的request,response提前過濾掉一些信息,或者提前設置一些參數,然后再傳入servlet或者struts的 action進行業務邏輯,比如過濾掉非法url(不是login.do的地址請求,如果用戶沒有登陸都過濾掉),或者在傳入servlet或者 struts的action前統一設置字符集,或者去除掉一些非法字符
攔截器,是在面向切面編程的就是在你的service或者一個方法,前調用一個方法,或者在方法后調用一個方法比如動態代理就是攔截器的簡單實現,在你調用方法前打印出字符串(或者做其它業務邏輯的操作),也可以在你調用方法后打印出字符串,甚至在你拋出異常的時候做業務邏輯的操作。
攔截器與過濾器的區別 :
- 攔截器是基於java的反射機制的,而過濾器是基於函數回調。
- 攔截器不依賴與servlet容器,過濾器依賴與servlet容器。
- 攔截器只能對action請求起作用,而過濾器則可以對幾乎所有的請求起作用。
- 攔截器可以訪問action上下文、值棧里的對象,而過濾器不能訪問。
- 在action的生命周期中,攔截器可以多次被調用,而過濾器只能在容器初始化時被調用一次
執行順序 :過濾前 - 攔截前 - Action處理 - 攔截后 - 過濾后。個人認為過濾是一個橫向的過程,首先把客戶端提交的內容進行過濾(例如未登錄用戶不能訪問內部頁面的處理);過濾通過后,攔截器將檢查用戶提交數據的驗證,做一些前期的數據處理,接着把處理后的數據發給對應的Action;Action處理完成返回后,攔截器還可以做其他過程(還沒想到要做啥),再向上返回到過濾器的后續操作。
一個Filter 可負責攔截多個請求或響應:一個請求或響應也可被多個請求攔截。
創建一個Filter 只需兩個步驟:
(1)創建Filter 處理類:
(2)在web.xml 文件中配置Filter 。
創建Filter 必須實現javax.servlet.Filter 接口,在該接口中定義了三個方法。
• void init(FilterConfig config): 用於完成Filter 的初始化。
• void destroy(): 用於Filter 銷毀前,完成某些資源的回收。
• void doFilter(ServletRequest request, ServletResponse response,FilterChain chain): 實現過濾功能,該方法就是對每個請求及響應增加的額外處理。
過濾器Filter也具有生命周期:init()->doFilter()->destroy(),由部署文件中的filter元素驅動。在servlet2.4中,過濾器同樣可以用於請求分派器,但須在web.xml中聲明,<dispatcher>INCLUDE或FORWARD或REQUEST或ERROR</dispatcher>該元素位於filter-mapping中。
Filter常用的場景:
例一、 日志的記錄,當有請求到達時,在該過濾器中進行日志的記錄。處理完成后,進入后續的Filter或者處理。
步驟1:編寫Filter類
package test.filter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
public class LogFilter implements Filter {
private FilterConfig config;
// 實現初始化方法
public void init(FilterConfig config) {
this.config = config;
}
// 實現銷毀方法
public void destroy() {
this.config = null;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
// 獲取ServletContext 對象,用於記錄日志
ServletContext context = this.config.getServletContext();
long before = System.currentTimeMillis();
System.out.println("開始過濾... ");
// 將請求轉換成HttpServletRequest 請求
HttpServletRequest hrequest = (HttpServletRequest) request;
// 記錄日志
context.log("Filter已經截獲到用戶的請求的地址: " + hrequest.getServletPath());
try {
// Filter 只是鏈式處理,請求依然轉發到目的地址。
chain.doFilter(request, response);
} catch (Exception e) {
e.printStackTrace();
}
long after = System.currentTimeMillis();
// 記錄日志
context.log("過濾結束");
// 再次記錄日志
context.log(" 請求被定位到" + ((HttpServletRequest) request).getRequestURI()
+ "所花的時間為: " + (after - before));
}
}
在上面的請求Filter中,僅在日志中記錄請求的URL,對所有的請求都執行chain.doFilter(request,reponse)方法,當Filter 對請求過濾后,依然將請求發送到目的地址。
步驟2:在web.xml中配置Filter
<!-- 定義Filter -->
<filter>
<!-- Filter 的名字 -->
<filter-name>log</filter-name>
<!-- Filter 的實現類 -->
<filter-class> test.filter.LogFilter</filter-class>
</filter>
<!-- 定義Filter 攔截地址 -->
<filter-mapping>
<!-- Filter 的名字 -->
<filter-name>log</filter-name>
<!-- Filter 負責攔截的URL -->
<url-pattern>/filter/*</url-pattern>
</filter-mapping>
