<?xml version="1.0" encoding="GBK"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN" "http://struts.apache.org/dtds/struts-2.1.7.dtd"> <struts> <!-- 通过常量配置Struts 2的国际化资源信息 --> <constant name="struts.custom.i18n.resources" value="mess"/> <!-- 通过常量配置Struts 2所使用的解码集--> <constant name="struts.i18n.encoding" value="GBK"/> <!-- 配置本系统所使用的包 --> <package name="lee" extends="struts-default"> <!-- 应用所需使用的拦截器都在该元素下配置 --> <interceptors> <!-- 配置mysimple拦截器 --> <interceptor name="mysimple" class="org.crazyit.app.interceptor.SimpleInterceptor"> <!-- 为拦截器指定参数值 --> <param name="name">简单拦截器</param> </interceptor> </interceptors> <action name="loginPro" class="org.crazyit.app.action.LoginAction"> <result name="error">/WEB-INF/content/error.jsp</result> <result name="success">/WEB-INF/content/welcome.jsp</result> <!-- 配置系统的默认拦截器 --> <interceptor-ref name="defaultStack"/> <!-- 应用自定义的mysimple拦截器 --> <interceptor-ref name="mysimple"> <param name="name">第一个</param> </interceptor-ref> <interceptor-ref name="mysimple"> <param name="name">第二个</param> </interceptor-ref> </action> <action name="*"> <result>/WEB-INF/content/{1}.jsp</result> </action> </package> </struts>
自定义实现的拦截器类:
package org.crazyit.app.interceptor; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import java.util.*; import org.crazyit.app.action.*; /** * Description: * <br/>网站: <a href="http://www.crazyit.org">疯狂Java联盟</a> * <br/>Copyright (C), 2001-2012, Yeeku.H.Lee * <br/>This program is protected by copyright laws. * <br/>Program Name: * <br/>Date: * @author Yeeku.H.Lee [email protected] * @version 1.0 */ public class SimpleInterceptor extends AbstractInterceptor { //简单拦截器的名字 private String name; //为该简单拦截器设置名字的setter方法 public void setName(String name) { this.name = name; } public String intercept(ActionInvocation invocation) throws Exception { //取得被拦截的Action实例 LoginAction action = (LoginAction)invocation.getAction(); //打印执行开始的实现 System.out.println(name + " 拦截器的动作---------" + "开始执行登录Action的时间为:" + new Date()); //取得开始执行Action的时间 long start = System.currentTimeMillis(); //执行该拦截器的后一个拦截器 //如果该拦截器后没有其他拦截器,则直接执行Action的execute方法 String result = invocation.invoke(); //打印执行结束的时间 System.out.println(name + " 拦截器的动作---------" + "执行完登录Action的时间为:" + new Date()); long end = System.currentTimeMillis(); System.out.println(name + " 拦截器的动作---------" + "执行完该Action的事件为" + (end - start) + "毫秒"); return result; } }
分析结果:(上面配置了2个拦截器)
结果小猜想其中的内部机制:
拦截前的操作:
struts2中的filter的StrutsPrepareAndExecuteFilter控制器会拦截用户请求的action,该filter会先先查看配置文件,发现有2个拦截器,从第一个拦截器开始,通过反射生成一个SimpleInterceptor对象,然后调用其intercept()方法,执行该方法中的invocation.invoke()方法,然后该invoke方法会通过interceptors.hasNext去检查是否还有后面的拦截器,于是发现还有第二个拦截器,继续通过反射生成第二个SimpleInterceptor对象,然后再intercept()→invocation.invoke()方法→intercept()→invoke()→递归检查,直到所有拦截器都递归完毕。因此会看到控制台有从第一个拦截器→第二个拦截器。
拦截后操作:
因此intercept()→invocation.invoke()方法→intercept()→invoke()→方法是递归实现,也就是栈结构,先进后出,因此当invoke方法执行完后,会按照这个栈结构继续运行,从而出现了拦截后先看到第二个拦截器→第一个拦截器。
进一步思考:
struts2中的filter的StrutsPrepareAndExecuteFilter控制器会拦截用户请求的action,该fiter会通过查看看action的配置文件,然后生成该action对应的拦截器Map或者List来保存所配置的拦截器的相关信息。同时也会产生一个ActionInvocation对象来控制action。其中这种控制就包刮拦截,然后调用ActionInvocation对象中的invoke()方法。通过查看实现了ActionInvocation类的代码中的invoke()方法,可以知道该invoke()方法是个递归方法。因此上面两个拦截前后的操作,应该只是在invoke()方法中完成,一直递归而已。其实这种递归是这样的invoke()→→intercept()→→invoke()→→如此循环。要知道ActionInvocation是控制action的,类似与jdk的动态代理(AOP思想),将拦截器的相关方法横切到相应的action,使各自的任务达到分离!
以上猜想再看下源代码果真是这样的:
DefaultActionInvocation:该类实现了ActionInvacation
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 = interceptors.next(); String interceptorMsg = "interceptor: " + interceptor.getName(); UtilTimerStack.push(interceptorMsg); try { resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); } finally { UtilTimerStack.pop(interceptorMsg); } } 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) { if (preResultListeners != null) { for (Object preResultListener : preResultListeners) { PreResultListener listener = (PreResultListener) preResultListener; String _profileKey = "preResultListener: "; try { UtilTimerStack.push(_profileKey); listener.beforeResult(this, resultCode); } finally { UtilTimerStack.pop(_profileKey); } } } // now execute the result, if we're supposed to if (proxy.getExecuteResult()) { executeResult(); } executed = true; } return resultCode; } finally { UtilTimerStack.pop(profileKey); } }