Struts2系列:(12)拦截器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Struts2系列:(12)拦截器相关的知识,希望对你有一定的参考价值。

学习拦截器

1、拦截器能够做什么?(Why 和 What)

2、如何定义拦截器?(How to use)

3、如何注册拦截器?(How to use)

4、如何使用拦截器?(How to use)



1、拦截器能够做什么?

Struts2 拦截器在访问某个 Action 方法之前或之后实时拦截的可插拔的组件。

拦截器栈(Interceptor Stack): 将拦截器按一定的顺序联结成一条链。

实现原理:当请求action时,Struts 查找配置文件,根据其配置实例化拦截器对象,并形成一个列表,依序逐个调用列表中的拦截器。


在struts2-core-2.x.x.jar中有一个struts-default.xml文件,在名为struts-default的package中定义了许多的interceptor和interceptor-stack。

    <package name="struts-default" abstract="true">
        <result-types>
            <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
            <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
            <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
            <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
            <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
            <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
            <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
            <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
            <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
            <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
            <result-type name="postback" class="org.apache.struts2.dispatcher.PostbackResult" />
        </result-types>

        <interceptors>
            <interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
            <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>
            <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
            <interceptor name="conversionError" class="org.apache.struts2.interceptor.StrutsConversionErrorInterceptor"/>
            <interceptor name="cookie" class="org.apache.struts2.interceptor.CookieInterceptor"/>
            <interceptor name="cookieProvider" class="org.apache.struts2.interceptor.CookieProviderInterceptor"/>
            <interceptor name="clearSession" class="org.apache.struts2.interceptor.ClearSessionInterceptor" />
            <interceptor name="createSession" class="org.apache.struts2.interceptor.CreateSessionInterceptor" />
            <interceptor name="debugging" class="org.apache.struts2.interceptor.debugging.DebuggingInterceptor" />
            <interceptor name="execAndWait" class="org.apache.struts2.interceptor.ExecuteAndWaitInterceptor"/>
            <interceptor name="exception" class="com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor"/>
            <interceptor name="fileUpload" class="org.apache.struts2.interceptor.FileUploadInterceptor"/>
            <interceptor name="i18n" class="com.opensymphony.xwork2.interceptor.I18nInterceptor"/>
            <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
            <interceptor name="modelDriven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
            <interceptor name="scopedModelDriven" class="com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor"/>
            <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
            <interceptor name="actionMappingParams" class="org.apache.struts2.interceptor.ActionMappingParametersInteceptor"/>
            <interceptor name="prepare" class="com.opensymphony.xwork2.interceptor.PrepareInterceptor"/>
            <interceptor name="staticParams" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
            <interceptor name="scope" class="org.apache.struts2.interceptor.ScopeInterceptor"/>
            <interceptor name="servletConfig" class="org.apache.struts2.interceptor.ServletConfigInterceptor"/>
            <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
            <interceptor name="token" class="org.apache.struts2.interceptor.TokenInterceptor"/>
            <interceptor name="tokenSession" class="org.apache.struts2.interceptor.TokenSessionStoreInterceptor"/>
            <interceptor name="validation" class="org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor"/>
            <interceptor name="workflow" class="com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor"/>
            <interceptor name="store" class="org.apache.struts2.interceptor.MessageStoreInterceptor" />
            <interceptor name="checkbox" class="org.apache.struts2.interceptor.CheckboxInterceptor" />
            <interceptor name="datetime" class="org.apache.struts2.interceptor.DateTextFieldInterceptor" />
            <interceptor name="profiling" class="org.apache.struts2.interceptor.ProfilingActivationInterceptor" />
            <interceptor name="roles" class="org.apache.struts2.interceptor.RolesInterceptor" />
            <interceptor name="annotationWorkflow" class="com.opensymphony.xwork2.interceptor.annotations.AnnotationWorkflowInterceptor" />
            <interceptor name="multiselect" class="org.apache.struts2.interceptor.MultiselectInterceptor" />
            <interceptor name="deprecation" class="org.apache.struts2.interceptor.DeprecationInterceptor" />

            <!-- Basic stack -->
            <interceptor-stack name="basicStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="datetime"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params"/>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="deprecation"/>
            </interceptor-stack>

            <!-- Sample validation and workflow stack -->
            <interceptor-stack name="validationWorkflowStack">
                <interceptor-ref name="basicStack"/>
                <interceptor-ref name="validation"/>
                <interceptor-ref name="workflow"/>
            </interceptor-stack>

            <!-- Sample file upload stack -->
            <interceptor-stack name="fileUploadStack">
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- Sample model-driven stack  -->
            <interceptor-stack name="modelDrivenStack">
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- Sample action chaining stack -->
            <interceptor-stack name="chainStack">
                <interceptor-ref name="chain"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- Sample i18n stack -->
            <interceptor-stack name="i18nStack">
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="basicStack"/>
            </interceptor-stack>

            <!-- An example of the paramsPrepareParams trick. This stack
                 is exactly the same as the defaultStack, except that it
                 includes one extra interceptor before the prepare interceptor:
                 the params interceptor.

                 This is useful for when you wish to apply parameters directly
                 to an object that you wish to load externally (such as a DAO
                 or database or service layer), but can‘t load that object
                 until at least the ID parameter has been loaded. By loading
                 the parameters twice, you can retrieve the object in the
                 prepare() method, allowing the second params interceptor to
                 apply the values on the object. -->
            <interceptor-stack name="paramsPrepareParamsStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="datetime"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="params"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params"/>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
            </interceptor-stack>

            <!-- A complete stack with all the common interceptors in place.
                 Generally, this stack should be the one you use, though it
                 may do more than you need. Also, the ordering can be
                 switched around (ex: if you wish to have your servlet-related
                 objects applied before prepare() is called, you‘d need to move
                 servletConfig interceptor up.

                 This stack also excludes from the normal validation and workflow
                 the method names input, back, and cancel. These typically are
                 associated with requests that should not be validated.
                 -->
            <interceptor-stack name="defaultStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="datetime"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params"/>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="debugging"/>
                <interceptor-ref name="deprecation"/>
            </interceptor-stack>

            <!-- The completeStack is here for backwards compatibility for
                 applications that still refer to the defaultStack by the
                 old name -->
            <interceptor-stack name="completeStack">
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>

            <!-- Sample execute and wait stack.
                 Note: execAndWait should always be the *last* interceptor. -->
            <interceptor-stack name="executeAndWaitStack">
                <interceptor-ref name="execAndWait">
                    <param name="excludeMethods">input,back,cancel</param>
                </interceptor-ref>
                <interceptor-ref name="defaultStack"/>
                <interceptor-ref name="execAndWait">
                    <param name="excludeMethods">input,back,cancel</param>
                </interceptor-ref>
            </interceptor-stack>

       </interceptors>

        <default-interceptor-ref name="defaultStack"/>

        <default-class-ref class="com.opensymphony.xwork2.ActionSupport" />
    </package>



2、如何自定义拦截器

实现拦截器,要实现Interceptor接口,但一般继承AbstractInterceptor抽象类。

自定义拦截器的步骤

(1)开发一个拦截器类 继承AbstractInterceptor,在intercept中实现拦截功能

    String intercept(ActionInvocation invocation)

(2)注册拦截器或者拦截链(拦截器按照定义的顺序执行)

(3)使用拦截链或者拦截器



下面是Interceptor接口的源代码:

package com.opensymphony.xwork2.interceptor;

import com.opensymphony.xwork2.ActionInvocation;

import java.io.Serializable;

public interface Interceptor extends Serializable {

    /**
     * Called to let an interceptor clean up any resources it has allocated.
     */
    void destroy();

    /**
     * Called after an interceptor is created, but before any requests are processed using
     * intercept , giving the Interceptor a chance to initialize any needed resources.
     */
    void init();

    /**
     * Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the
     * request by the ActionInvocation or to short-circuit the processing and just return a String return code.
     *
     * @param invocation the action invocation
     * @return the return code, either returned from ActionInvocation.invoke(), or from the interceptor itself.
     * @throws Exception any system-level error, as defined in com.opensymphony.xwork2.Action.execute().
     */
    String intercept(ActionInvocation invocation) throws Exception;

}

Interceptor 接口

每个拦截器都实现com.opensymphony.xwork2.interceptor.Interceptor接口

init(): 该方法将在拦截器被创建后立即被调用, 只被调用一次. 可对相关资源进行必要初始化

destroy(): 该方法将在拦截器被销毁之前被调用, 只被调用一次. 

intercept(ActionInvocation invocation) : 每拦截一个Action请求, 该方法被调用一次, 传递一个 ActionInvocation 接口对象. 


ActionInvocation代表一个给定动作的执行状态, 拦截器可从该对象里获取该动作相关联Action 对象和 Result 对象;完成拦截器任务后, 拦截器将调用 ActionInvocation 的 invoke 方法进入到 Action 处理流程的下一环节. 

com.opensymphony.xwork2.ActionInvocation接口

An ActionInvocation represents the execution state of an Action. It holds the Interceptors and the Action instance. By repeated re-entrant execution of the invoke() method, initially by the ActionProxy, then by the Interceptors, the Interceptors are all executed, and then the Action and the Result.



下面是AbstractInterceptor抽象类的源代码:

package com.opensymphony.xwork2.interceptor;

import com.opensymphony.xwork2.ActionInvocation;

/**
 * Provides default implementations of optional lifecycle methods
 */
public abstract class AbstractInterceptor implements Interceptor {

    /**
     * Does nothing
     */
    public void init() {
    }
    
    /**
     * Does nothing
     */
    public void destroy() {
    }


    /**
     * Override to handle interception
     */
    public abstract String intercept(ActionInvocation invocation) throws Exception;
}

AbstractInterceptor 抽象类实现 Interceptor 接口. 为 init、destroy 提供空白实现

com.opensymphony.xwork2.interceptor.AbstractInterceptor抽象类

Provides default implementations of optional lifecycle methods



自定义拦截器示例:对未登录的用户进行拦截AuthorityInterceptor.java

package com.rk.strut.g_intercepter;

import java.util.Map;

import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class AuthorityInterceptor extends AbstractInterceptor
{

	@Override
	public String intercept(ActionInvocation invocation) throws Exception
	{
		ActionContext context = invocation.getInvocationContext();
		Map<String, Object> session = context.getSession();
		Object user = session.get("user");
		if(user != null)
		{
			return invocation.invoke();
		}
		return Action.LOGIN;
	}

}



3、如何注册拦截器、如何使用拦截器

将拦截器在package中进行注册,可以在package中使用拦截器,也可以在action中使用拦截器。


在package中使用拦截器

<package name="authority" extends="struts-default">  
	<interceptors>  
		<!--定义一个名为authority的拦截器-->  
		<interceptor class="com.rk.struts.interceptor.AuthorityInterceptor" name="authority"/>  
		<!--定义一个包含权限检查的拦截器栈-->  
		<interceptor-stack name="mydefault">  
			<!--配置内建默认拦截器-->  
			<interceptor-ref name="defaultStack"/>  
			<!--配置自定义的拦截器-->  
			<interceptor-ref name="authority"/>  
		</interceptor-stack>  
	</interceptors>  
	<!-- 使用拦截链 -->
	<default-interceptor-ref name="mydefault" />                          
	<action name="add" class="com.future.power.AddAction"  method="add">  
		<result name="success">/add.jsp</result>  
	</action>
</package>


在action中使用拦截器

    <package name="interceptorPackage" namespace="/rk/interceptor" extends="struts-default">
    	<interceptors>
    		<interceptor name="authority" class="com.rk.strut.g_intercepter.AuthorityInterceptor"></interceptor>
    		<interceptor name="myip" class="com.rk.strut.g_intercepter.IPInterceptor"></interceptor>
    	</interceptors>
    	
		<action name="student_*" class="com.rk.strut.g_intercepter.StudentAction" method="{1}">
			<interceptor-ref name="myip"></interceptor-ref>
			<interceptor-ref name="authority"></interceptor-ref>
			<result name="success">/interceptor/student.jsp</result>
            <result name="login">/interceptor/login.jsp</result>
            <result name="ip">/interceptor/ip.jsp</result>
		</action>
        <action name="login" class="com.rk.strut.g_intercepter.LoginAction">
            <result name="success">/interceptor/loginSuccess.jsp</result>
        </action>
    </package>	


4、补充


4.1、Interceptor文档

com.opensymphony.xwork2.interceptor.Interceptor接口


(1)拦截器介绍

Interceptors are objects that dynamically intercept Action invocations. (a)They provide the developer with the opportunity to define code that can be executed before and/or after the execution of an action. (b)They also have the ability to prevent an action from executing. Interceptors provide developers a way to encapulate common functionality in a reusable form that can be applied to one or more Actions.


Interceptors must be stateless and not assume that a new instance will be created for each request or Action. Interceptors may choose to either short-circuit the ActionInvocation execution and return a return code (such as com.opensymphony.xwork2.Action.SUCCESS), or it may choose to do some processing before and/or after delegating the rest of the procesing using ActionInvocation.invoke().

【拦截器必须是stateless。拦截器在处理request和action的时候只创建一次实例。】 



(2)如何在struts.xml文件中使用拦截器?

Interceptor‘s parameter could be overriden through the following ways :- 


Method 1: 


 <action name="myAction" class="myActionClass">

     <interceptor-ref name="exception"/>

     <interceptor-ref name="alias"/>

     <interceptor-ref name="params"/>

     <interceptor-ref name="servletConfig"/>

     <interceptor-ref name="prepare"/>

     <interceptor-ref name="i18n"/>

     <interceptor-ref name="chain"/>

     <interceptor-ref name="modelDriven"/>

     <interceptor-ref name="fileUpload"/>

     <interceptor-ref name="staticParams"/>

     <interceptor-ref name="params"/>

     <interceptor-ref name="conversionError"/>

     <interceptor-ref name="validation">

     <param name="excludeMethods">myValidationExcudeMethod</param>

     </interceptor-ref>

     <interceptor-ref name="workflow">

     <param name="excludeMethods">myWorkflowExcludeMethod</param>

     </interceptor-ref>

 </action>

 

Method 2: 


 <action name="myAction" class="myActionClass">

   <interceptor-ref name="defaultStack">

     <param name="validation.excludeMethods">myValidationExcludeMethod</param>

     <param name="workflow.excludeMethods">myWorkflowExcludeMethod</param>

   </interceptor-ref>

 </action>

 



In the first method, the whole default stack is copied and the parameter then changed accordingly. 




In the second method, the ‘interceptor-ref‘ refer to an existing interceptor-stack, namely defaultStack in this example, and override the validator and workflow interceptor excludeMethods typically in this case. Note that in the ‘param‘ tag, the name attribute contains a dot (.). The word before the dot(.) specifies the interceptor name whose parameter is to be overridden and the word after the dot (.) specifies the parameter itself. Essetially it is as follows :- 



    <interceptor-name>.<parameter-name>

 

Note also that in this case the ‘interceptor-ref‘ name attribute is used to indicate an interceptor stack which makes sense as if it is referring to the interceptor itself it would be just using Method 1 describe above. 



(3)嵌套的拦截器的使用

Nested Interceptor param overriding 



Interceptor stack parameter overriding could be nested into as many level as possible, though it would be advisable not to nest it too deep as to avoid confusion, For example, 


 <interceptor name="interceptor1" class="foo.bar.Interceptor1" />

 <interceptor name="interceptor2" class="foo.bar.Interceptor2" />

 <interceptor name="interceptor3" class="foo.bar.Interceptor3" />

 <interceptor name="interceptor4" class="foo.bar.Interceptor4" />

 <interceptor-stack name="stack1">

     <interceptor-ref name="interceptor1" />

 </interceptor-stack>

 <interceptor-stack name="stack2">

     <interceptor-ref name="intercetor2" />

     <interceptor-ref name="stack1" />

 </interceptor-stack>

 <interceptor-stack name="stack3">

     <interceptor-ref name="interceptor3" />

     <interceptor-ref name="stack2" />

 </interceptor-stack>

 <interceptor-stack name="stack4">

     <interceptor-ref name="interceptor4" />

     <interceptor-ref name="stack3" />

  </interceptor-stack>

 

Assuming the interceptor has the following properties Interceptor property 

Interceptor1 param1 

Interceptor2 param2 

Interceptor3 param3 

Interceptor4 param4 

We could override them as follows :- 

    <action ... >

        <!-- to override parameters of interceptor located directly in the stack  -->

        <interceptor-ref name="stack4">

           <param name="interceptor4.param4"> ... </param>

        </interceptor-ref>

    </action>

 

    <action ... >

        <!-- to override parameters of interceptor located under nested stack -->

        <interceptor-ref name="stack4">

            <param name="stack3.interceptor3.param3"> ... </param>

            <param name="stack3.stack2.interceptor2.param2"> ... </param>

            <param name="stack3.stack2.stack1.interceptor1.param1"> ... </param>

        </interceptor-ref>

    </action>


4.2、ActionInvocation、ActionProxy、ActionContext、Action


ActionInvocation

com.opensymphony.xwork2.ActionInvocation

An ActionInvocation represents the execution state of an Action【ActionInvocation代表了Action的执行状态】. It holds the Interceptors and the Action instance【ActionInvocation包括了Interceptors和Action的实例。】. By repeated re-entrant execution of the invoke() method, initially by the ActionProxy, then by the Interceptors, the Interceptors are all executed, and then the Action and the Result.




ActionProxy

com.opensymphony.xwork2.ActionProxy

ActionProxy is an extra layer between XWork and the action so that different proxies are possible. 




ActionContext

com.opensymphony.xwork2.ActionContext

The ActionContext is the context in which an Action is executed【ActionContext是Action的运行环境】. Each context is basically a container of objects an action needs for execution like the session, parameters, locale, etc.

The ActionContext is thread local which means that values stored in the ActionContext are unique per thread. The benefit of this is you don‘t need to worry about a user specific action context, you just get it: 

ActionContext context = ActionContext.getContext();

Finally, because of the thread local usage you don‘t need to worry about making your actions thread safe.


ValueStack

com.opensymphony.xwork2.util.ValueStack

ValueStack allows multiple beans to be pushed in【将多个bean压入的ValueStack中】 and dynamic EL expressions【dynamic EL expression,不太理解】 to be evaluated against it. 【下面讲述了evaluating an expression的执行过程】When evaluating an expression, the stack will be searched down the stack, from the latest objects pushed in to the earliest, looking for a bean with a getter or setter for the given property or a method of the given name (depending on the expression being evaluated).


Action

com.opensymphony.xwork2.Action

All actions may implement this interface, which exposes the execute() method. 


Result

com.opensymphony.xwork2.Result

All results (except for Action.NONE) of an Action are mapped to a View implementation. 


StrutsStatics

Constants used by Struts. The constants can be used to get or set objects out of the action context or other collections. 


Example: 

    ActionContext.getContext().put(HTTP_REQUEST, request);

or 

    ActionContext context = ActionContext.getContext();

    HttpServletRequest request = (HttpServletRequest)context.get(HTTP_REQUEST);



从左边的对象,可以得到右边的对象

ActionInvocation-->Action

ActionInvocation-->ActionContext

ActionInvocation-->ActionProxy

ActionInvocation-->ValueStack


ActionProxy-->Action

ActionProxy-->ActionInvocation


ActionContext-->ActionInvocation

ActionContext-->ActionContext

ActionContext-->ValueStack



以上是关于Struts2系列:(12)拦截器的主要内容,如果未能解决你的问题,请参考以下文章

Struts2-学习笔记系列-PreResultListener

Struts2系列:(13)防表单重复提交(token + 拦截器)

Struts2系列:(11)文件上传

SSH框架之Struts2系列

struts2官方 中文教程 系列九:Debugging Struts

Struts2的拦截器