攔截器(interceptor)是Struts2最強大的特性之一,也可以說是struts2的核心,攔截器可以讓你在Action和result被執行之前或之後進行一些處理。同時,攔截器也可以讓你將通用的代碼模塊化並作爲可重用的類。Struts2中的很多特性都是由攔截器來完成的。攔截是AOP的一種實現策略。在Webwork的中文文檔的解釋爲:攔截器是動態攔截Action調用的對象。它提供了一種機制可以使開發者可以定義在一個action執行的前後執行的代碼,也可以在一個action執行前阻止其執行。同時也是提供了一種可以提取action中可重用的部分的方式。談到攔截器,還有一個詞大家應該知道——攔截器鏈(Interceptor
Chain,在Struts 2中稱爲攔截器棧Interceptor
Stack)。攔截器鏈就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,攔截器鏈中的攔截器就會按其之前定義的順序被調用
爲了便於自身理解,我們自己實現一個。
在我們的請求到來之前,加入現在我進入了一個頁面,而改頁面是需要先檢查是否已登錄,此時如果我加一個判斷的話,就可以很容易實現,但是當我們的頁面增多時,那麼就該爲我們的每個請求頁面都加個判斷,是相當麻煩的一件事,故此,spring或struts都爲我們提供了相應的攔截器,以下是攔截器的實現原理:
首先我們需要定義一個action接口
public interface Action {
String execute();
}
攔截器接口
public interface Interceptor {
void before(ActionInvocation invocation);
String intercept(ActionInvocation invocation);
void after(ActionInvocation invocation);
}
調度器接口
public interface ActionInvocation {
String invoke();
}
定義我們的攔截器
AroundInterceptor 攔截器
package com.cmh.intercept;
import com.cmh.service.ActionInvocation;
public class AroundInterceptor implements Interceptor{
@Override
public void before(ActionInvocation invocation) {
// TODO Auto-generated method stub
System.out.println("before:"+"AroundInterceptor");
}
@Override
public String intercept(ActionInvocation invocation) {
// TODO Auto-generated method stub
before(invocation);
String result = invocation.invoke();
after(invocation);
return result;
}
@Override
public void after(ActionInvocation invocation) {
// TODO Auto-generated method stub
System.out.println("after:"+"AroundInterceptor");
}
}
ExceptionInterceptor攔截器
package com.cmh.intercept;
import com.cmh.service.ActionInvocation;
public class ExceptionInterceptor implements Interceptor{
@Override
public void before(ActionInvocation invocation) {
// TODO Auto-generated method stub
System.out.println("before:"+"ExceptionInterceptor");
}
@Override
public String intercept(ActionInvocation invocation) {
// TODO Auto-generated method stub
before(invocation);
String result = invocation.invoke();
after(invocation);
return result;
}
@Override
public void after(ActionInvocation invocation) {
// TODO Auto-generated method stub
System.out.println("after:"+"ExceptionInterceptor");
}
}
I18NInterceptor攔截器
package com.cmh.intercept;
import com.cmh.service.ActionInvocation;
public class I18NInterceptor implements Interceptor{
@Override
public void before(ActionInvocation invocation) {
// TODO Auto-generated method stub
System.out.println("before:"+"I18NInterceptor");
}
@Override
public String intercept(ActionInvocation invocation) {
// TODO Auto-generated method stub
before(invocation);
String result = invocation.invoke();
after(invocation);
return result;
}
@Override
public void after(ActionInvocation invocation) {
// TODO Auto-generated method stub
System.out.println("after:"+"I18NInterceptor");
}
}
調度器實現類DefaultActionInvoation
package com.cmh.service;
import java.util.ArrayList;
import java.util.List;
import com.cmh.action.Action;
import com.cmh.intercept.Interceptor;
public class DefaultActionInvoation implements ActionInvocation{
int index = 0;
private Action action;
private List<Interceptor> interceptors = new ArrayList<Interceptor>();
/**
* Get action
* @return Action the action
*/
public Action getAction() {
return action;
}
/**
* Set action
* @param action Action the action to set
*/
public void setAction(Action action) {
this.action = action;
}
/**
* Set interceptors
* @param interceptors List<Interceptor> the interceptors to set
*/
public void addInterceptor(Interceptor interceptors) {
this.interceptors.add(interceptors);
}
@Override
public String invoke() {//遞歸調用
// TODO Auto-generated method stub
String result = "";
if(index == interceptors.size()){
result = action.execute();
}else{
Interceptor interceptor =interceptors.get(index);
index++;
result = interceptor.intercept(this);
}
return result;
}
}
測試類
public static void test3(){
Interceptor exptionInterceptor = new ExceptionInterceptor();
Interceptor i18nInterceptor = new I18NInterceptor();
Interceptor aroundInterceptor = new AroundInterceptor();
DefaultActionInvoation actionInvocation = new DefaultActionInvoation();
actionInvocation.addInterceptor(exptionInterceptor);
actionInvocation.addInterceptor(i18nInterceptor);
actionInvocation.addInterceptor(aroundInterceptor);
Action action = new HelloWorldAction();
actionInvocation.setAction(action);
String result = actionInvocation.invoke();
System.out.println("Action result:" + result);
}
測試結果
由以上代碼不難看出,當我們的請求來到時,首先會經DefaultActionInvoation 的invoke處理,調用的次序依次爲
ExceptionInterceptor->I18NInterceptor->AroundInterceptor->AroundInterceptor->I18NInterceptor->ExceptionInterceptor
最後纔將我們的結果給弄出來,以上爲一個攔截器最基本的原理。下面我們來看看struts2中的原代碼及流程
strtus2的流程圖
我們再來看看他的源代碼
以下是DefaultActionInvoation 中的invoke的源代碼
/**
* @throws ConfigurationException If no result can be found with the returned code
*/
public String invoke() throws Exception {
String profileKey = "invoke: ";
try {
UtilTimerStack.push(profileKey);
if (executed) {
throw new IllegalStateException("Action has already executed");
}
// 依次調用攔截器堆棧中的攔截器代碼執行
if (interceptors.hasNext()) {
final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
UtilTimerStack.profile("interceptor: "+interceptor.getName(),
new UtilTimerStack.ProfilingBlock<String>() {
public String doProfiling() throws Exception {
// 將ActionInvocation作爲參數,調用interceptor中的intercept方法執行
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
return null;
}
});
} else {
resultCode = invokeActionOnly();
}
// this is needed because the result will be executed, then control will return to the Interceptor, which will
// return above and flow through again
if (!executed) {
// 執行PreResultListener
if (preResultListeners != null) {
for (Iterator iterator = preResultListeners.iterator();
iterator.hasNext();) {
PreResultListener listener = (PreResultListener) iterator.next();
String _profileKey="preResultListener: ";
try {
UtilTimerStack.push(_profileKey);
listener.beforeResult(this, resultCode);
}
finally {
UtilTimerStack.pop(_profileKey);
}
}
}
// now execute the result, if we're supposed to
// action與interceptor執行完畢,執行Result
if (proxy.getExecuteResult()) {
executeResult();
}
executed = true;
}
return resultCode;
}
finally {
UtilTimerStack.pop(profileKey);
}
}
攔截器的源代碼
public String intercept(ActionInvocation invocation) throws Exception {
String result = null;
before(invocation);
// 調用下一個攔截器,如果攔截器不存在,則執行Action
result = invocation.invoke();
after(invocation, result);
return result;
}
struts的攔截器最大歸功於他的動態代理模式,因爲我們的action具體類都是一個繼承動態代理的類。所以這就不必爲每一個類都加相應的邏輯,只需繼承就行了。