从软件架构的角度讲:拦截器属于AOP编程的范畴。它将影响了多个业务对象的公共行为封装到一个个可重用的模块,减少了系统的重复代码,实现功能的高度内聚,确保了业务对象 的整洁和纯度。
从java代码的角度讲:它就是一个普度的Java对象,它只需要实现一个名为Interceptor的接口。
当我们在struts.xml配置文件中包含struts-default包时,我们就会拥有默认的拦截器和拦截器栈。一个拦截器栈包含一组拦截器。堆栈中的每个拦截器使用interceptor-ref元素定义。
拦截器的工作原理:
当框架接收到一个请求的时候,它首先必须决定这个URL映射到哪一个动作组件。这个动作组件的一个实例就会被加入到一个新创建的ActionInvocation实例中。接着框架咨询声明性架构(XML配置文件或Java注解),以发现哪一些拦截器应该被触发,以及按照什么样的顺序触发。将这些拦截器的引用添加到ActionInvocation中。除了这些核心元素,ActionInvocation也拥有其他重要的信息(Servlet请求对象和当前动作组件可用的结果组件列表)。
当ActionInvocation被创建完毕,并且填充了需要的所有对象和信息,就可以被使用了。ActionInvocation公开了一个invoke()方法,框架通过调用这个方法开始动作的执行。当框架调用了这个方法时,ActionInvocation通过执行拦截器栈中的第一个拦截器开始这个调用过程。需要注意的是,invoke()并不是总是指向拦截器栈的第一个拦截器,ActionInvocation负责跟踪执行过程达到的状态,并且把控制交给栈中合适的拦截器(通过调用拦截器的intercept()方法将控制权交给拦截器)。通过递归调用(递归过程?框架通过第一次调用ActionInvocation对象的invoke()方法开始了这个过程。ActionInvocation通过调用拦截器的intercept()方法把控制权交给第一个拦截器。重要的是,intercept()方法把ActionInvocation实例作为一个参数。在拦截器的处理过程中,他会调用ActionInvocation实例参数的invoke()方法来继续调用后续拦截器的递归过程。因此,在通常的执行中,调用过程向下通过所有拦截器,直到栈中再也没有拦截器时,触发动作。另外,ActionInvocation在内部管理处理状态,因此它总是直到自己现在处在栈的什么位置。)ActionInvocation的invoke()方法,拦截器栈中后续的拦截器继续执行,最终执行动作。这是因为每一次invoke()方法被调用时,ActionInvocation都会查询自身的状态,调用接下来的拦截器。在所有拦截器都被调用之后,invoke()方法会促使动作类执行。
执行顺序:
在提交数据到框架时,框架调用拦截器的过程,首先框架会根据URL请求创建指定的动作TestAction,将TestAction的实例和TestAction相关的拦截器引用myStack放入一个新的ActionInvocation对象中(还包含其他信息),然后框架调用ActionInvocation的invoke()方法,此时开始了拦截器栈调用过程,最开始调用拦截器栈的第一个拦截器也就是Intercept1,拦截器执行完预处理后,因为intercept()方法接收一个ActionInvocation对象作为参数,在Intercept1.intercept()方法中继续调用 ActionInvocation对象的invoke()方法将向下继续调用栈中余下的拦截器Intercept2...一直到栈中没有拦截器为止,最后执行动作组件。在结果被呈现之后,拦截器会按照相反的顺序再触发一遍,使他们可以进行后处理。
拦截器工作原理图:
拦截器触发时能够做些什么?
1. 做一些预处理。在这个阶段拦截器可以用来准备、过滤、改变或者操作任何可以访问的重要数据。这些数据包括所有与当前请求相关的关键对象和数据,也包括动作。
2. 通过调用invoke()方法将控制转移给后续的拦截器,直到动作。或者通过返回一个控制字符串中断执行。在这个阶段,如果拦截器决定请求不应该继续,他可以不调用ActionInvocation实例上的invoke()方法,而是直接返回一个控制字符串。通过这种方式可以停止后续的执行,并且决定哪个结果被呈现。
3. 做一些后加工。在这个阶段,任何一个返回的拦截器可以修改可以访问的对象的数据作为后加工,但是此时结果已经确定了。
查看dtd文档可以看到interceptors标签要定义在package标签下,我们对照struts-default.xml可以看到interceptors标签确实定义在package标签下,并且在dtd文档中package元素右边说明中interceptors标签右边带有一个"?"符号,表示该标签要么有且只有1个,要么没有。
再接着查看dtd文档interceptors元素右面有"(interceptor|interceptor-stack)*"的一串字符式子,表示interceptors标签下可以有0个或多个interceptor标签和interceptor-stack标签,并且不分顺序。
在dtd文档中可以看到interceptor-stack的name属性为#REQUIRED表示这是必须的属性,你声明一个拦截器栈必须给栈取一个名字。而interceptor标签中有两个属性name和class它们都是#REQUIRED,所以你想定义一个拦截器就必须指明这两个属性的值,name为拦截器的名字,class为这个拦截器会使用哪一个类作为它的处理器。还有interceptor-stack标签可以包含许多interceptor-ref标签,这些标签是用于引用你使用interceptor标签声明的拦截器或另外的拦截器栈,它的name属性的值为interceptor标签声明的拦截器的name值或其他拦截器栈的name值,该属性也是#REQUIRED。
最后一个package可以定义一组默认拦截器(有且只能有1个,dtd规定) ,例如在struts-default.xml文档中
<default-interceptor-ref name="defaultStack"/>
定义了默认的拦截器组,这个默认的拦截器组会与这个包内没有显示声明自己的拦截器的所有动作相关联。
下面为Struts 2为我们提供的struts-default.xml文件部分:
1 <struts> 2 ... 3 <interceptors> 4 <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/> 5 <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/> 6 <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/> 7 <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/> 8 <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/> 9 <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor"/> 10 <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor"/> 11 <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor"/> 12 <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/> 13 <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/> 14 <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/> 15 <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/> 16 <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/> 17 <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/> 18 <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/> 19 <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/> 20 <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/> 21 <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/> 22 <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/> 23 <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/> 24 <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/> 25 <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/> 26 <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/> 27 <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/> 28 <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/> 29 <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/> 30 <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor"/> 31 <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor"/> 32 <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor"/> 33 <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor"/> 34 <interceptor name="jsonValidation" class="org.apache.struts2.interceptor.validation.JSONValidationInterceptor"/> 35 <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor"/> 36 <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor"/> 37 38 <!-- Basic stack --> 39 <interceptor-stack name="basicStack"> 40 <interceptor-ref name="exception"/> 41 <interceptor-ref name="servletConfig"/> 42 <interceptor-ref name="prepare"/> 43 <interceptor-ref name="checkbox"/> 44 <interceptor-ref name="multiselect"/> 45 <interceptor-ref name="actionMappingParams"/> 46 <interceptor-ref name="params"> 47 <param name="excludeParams">dojo\..*,^struts\..*</param> 48 </interceptor-ref> 49 <interceptor-ref name="conversionError"/> 50 </interceptor-stack> 51 52 <!-- Sample validation and workflow stack --> 53 <interceptor-stack name="validationWorkflowStack"> 54 <interceptor-ref name="basicStack"/> 55 <interceptor-ref name="validation"/> 56 <interceptor-ref name="workflow"/> 57 </interceptor-stack> 58 59 <!-- Sample JSON validation stack --> 60 <interceptor-stack name="jsonValidationWorkflowStack"> 61 <interceptor-ref name="basicStack"/> 62 <interceptor-ref name="validation"> 63 <param name="excludeMethods">input,back,cancel</param> 64 </interceptor-ref> 65 <interceptor-ref name="jsonValidation"/> 66 <interceptor-ref name="workflow"/> 67 </interceptor-stack> 68 69 <!-- Sample file upload stack --> 70 <interceptor-stack name="fileUploadStack"> 71 <interceptor-ref name="fileUpload"/> 72 <interceptor-ref name="basicStack"/> 73 </interceptor-stack> 74 75 <!-- Sample model-driven stack --> 76 <interceptor-stack name="modelDrivenStack"> 77 <interceptor-ref name="modelDriven"/> 78 <interceptor-ref name="basicStack"/> 79 </interceptor-stack> 80 81 <!-- Sample action chaining stack --> 82 <interceptor-stack name="chainStack"> 83 <interceptor-ref name="chain"/> 84 <interceptor-ref name="basicStack"/> 85 </interceptor-stack> 86 87 <!-- Sample i18n stack --> 88 <interceptor-stack name="i18nStack"> 89 <interceptor-ref name="i18n"/> 90 <interceptor-ref name="basicStack"/> 91 </interceptor-stack> 92 ... 93 94 </interceptors> 95 96 <default-interceptor-ref name="defaultStack"/> 97 98 <default-class-ref class="com.opensymphony.xwork2.ActionSupport"/> 99 </package> 100 101 </struts>
如何定义拦截器:实现Interceptor接口;创建自定义的拦截器还可以扩展com.opensymphony.xwork2.interceptor.AbstractInterceptor类,它并没有什么高级的地方,它仅仅只是帮我们实现了Interceptor接口,帮我们默认实现了init()和destory()方法
下面演示一个简单的权限验证的例子:
它的工作原理很简单,当一个请求访问一个安全动作时,我们想检查请求是否是一个通过身份验证的用户发出的。
权限认证拦截器:
1 public class AuthenticationInterceptor implements Interceptor 2 { 3 private static final long serialVersionUID = -1500368808387165682L; 4 5 public void destroy() 6 { 7 8 } 9 10 public void init() 11 { 12 13 } 14 15 public String intercept(ActionInvocation invocation) throws Exception 16 { 17 Map<String , Object> session = invocation.getInvocationContext().getSession(); 18 19 User user = (User) session.get("USER"); 20 21 if (user == null) 22 { 23 return Action.INPUT; 24 } 25 else 26 { 27 Action action = (Action) invocation.getAction(); 28 if (action instanceof UserAware) 29 { 30 ((UserAware) action).setUser(user); 31 } 32 } 33 34 return invocation.invoke(); 35 } 36 }
最后,Struts 2内置了很多的日常Web开发都会用到的拦截器,所以一般不太需要自己开发一个拦截器,内置的拦截器几乎包含了所有日常需要的功能,要想真正运用好Struts 2必须要了解他们的工作原理,在Struts 2的官方网站上有详细的介绍,可以去哪里找到你需要的东西。
参考文献:
http://www.cnblogs.com/suxiaolei/archive/2011/10/28/2228063.html