`

《Struts 2 in Action》读书笔记——part 2——核心概念之拦截器

阅读更多

第四章    使用拦截器追加工作流

4.1 为什么要拦截请求
在尽量隔离WEB应用的程序关注点时,拦截器极大地提高了分离水平。特别是,拦截器消除了Action组件中的横切任务。


4.2 拦截器的工作原理
框架不直接调用Action的execute()方法,而是创建一个叫做ActionInvocation的对象,它封装了Action执行相关的所有处理细节。

public interface ActionInvocation extends Serializable {

    /**
     * Get the Action associated with this ActionInvocation.
     *
     * @return the Action
     */
    Object getAction();

    /**
     * Gets whether this ActionInvocation has executed before.
     * This will be set after the Action and the Result have executed.
     *
     * @return <tt>true</tt> if this ActionInvocation has executed before.
     */
    boolean isExecuted();

    /**
     * Gets the ActionContext associated with this ActionInvocation. The ActionProxy is
     * responsible for setting this ActionContext onto the ThreadLocal before invoking
     * the ActionInvocation and resetting the old ActionContext afterwards.
     *
     * @return the ActionContext.
     */
    ActionContext getInvocationContext();

    /**
     * Get the ActionProxy holding this ActionInvocation.
     *
     * @return the ActionProxy.
     */
    ActionProxy getProxy();

    /**
     * If the ActionInvocation has been executed before and the Result is an instance of {@link ActionChainResult}, this method
     * will walk down the chain of <code>ActionChainResult</code>s until it finds a non-chain result, which will be returned. If the
     * ActionInvocation's result has not been executed before, the Result instance will be created and populated with
     * the result params.
     *
     * @return the result.
     * @throws Exception can be thrown.
     */
    Result getResult() throws Exception;

    /**
     * Gets the result code returned from this ActionInvocation.
     *
     * @return the result code
     */
    String getResultCode();

    /**
     * Sets the result code, possibly overriding the one returned by the
     * action.
     * <p/>
     * The "intended" purpose of this method is to allow PreResultListeners to
     * override the result code returned by the Action.
     * <p/>
     * If this method is used before the Action executes, the Action's returned
     * result code will override what was set. However the Action could (if
     * specifically coded to do so) inspect the ActionInvocation to see that
     * someone "upstream" (e.g. an Interceptor) had suggested a value as the
     * result, and it could therefore return the same value itself.
     * <p/>
     * If this method is called between the Action execution and the Result
     * execution, then the value set here will override the result code the
     * action had returned.  Creating an Interceptor that implements
     * {@link PreResultListener} will give you this oportunity.
     * <p/>
     * If this method is called after the Result has been executed, it will
     * have the effect of raising an IllegalStateException.
     *
     * @param resultCode  the result code.
     * @throws IllegalStateException if called after the Result has been executed.
     * @see #isExecuted()
     */
    void setResultCode(String resultCode);

    /**
     * Gets the ValueStack associated with this ActionInvocation.
     *
     * @return the ValueStack
     */
    ValueStack getStack();

    /**
     * Register a {@link PreResultListener} to be notified after the Action is executed and
     * before the Result is executed.
     * <p/>
     * The ActionInvocation implementation must guarantee that listeners will be called in
     * the order in which they are registered.
     * <p/>
     * Listener registration and execution does not need to be thread-safe.
     *
     * @param listener the listener to add.
     */
    void addPreResultListener(PreResultListener listener);

    /**
     * Invokes the next step in processing this ActionInvocation.
     * <p/>
     * If there are more Interceptors, this will call the next one. If Interceptors choose not to short-circuit
     * ActionInvocation processing and return their own return code, they will call invoke() to allow the next Interceptor
     * to execute. If there are no more Interceptors to be applied, the Action is executed.
     * If the {@link ActionProxy#getExecuteResult()} method returns <tt>true</tt>, the Result is also executed.
     *
     * @throws Exception can be thrown.
     * @return the return code.
     */
    String invoke() throws Exception;

    /**
     * Invokes only the Action (not Interceptors or Results).
     * <p/>
     * This is useful in rare situations where advanced usage with the interceptor/action/result workflow is
     * being manipulated for certain functionality.
     *
     * @return the return code.
     * @throws Exception can be thrown.
     */
    String invokeActionOnly() throws Exception;

    /**
     * Sets the action event listener to respond to key action events.
     *
     * @param listener the listener.
     */
    void setActionEventListener(ActionEventListener listener);

    void init(ActionProxy proxy) ;

}

 

当框架收到一个请求时,它首先决定这个URL映射到哪个Action。这个Action的一个实例会被加入到一个新创建的ActionInvocation实例中。接着,框架咨询声明性架构(通过应用程序的XML或者Java注解),以发现哪些拦截器应该触发,以及按什么顺序触发。除了这些核心元素,ActionInvocation还拥有对其他重要信息(例如Servlet请求对象和当前Action可用的结果组件的映射)的引用。


ActionInvocation公开了invoke()方法,框架通过调用这个方法开始Action的执行。当框架调用这个方法时,ActionInvocation通过执行拦截器栈中第一个拦截器开始这个调用过程。注意,invoke()方法并不总是映射到第一个拦截器,ActionInvocation负责跟踪执行过程到达的状态,并且通过调用拦截器的intercept()方法把控制权交给栈中合适的拦截器。重要的是,intercept()方法把ActionInvocation实例作为一个参数。

public String intercept(ActionInvocation invocation) throws Exception {
...
}

 

后续拦截器的继续执行,最终执行Action,这些都是通过递归调用ActionInvocation的invoke()方法实现。每次invoke()方法被调用时,ActionInvocation都会查询自身的状态,调用接下来的任何一个拦截器。



 
因此,在通常的执行中,调用过程向下通过所有拦截器,直到栈中再也没有拦截器时,触发Action。另外,ActionInvocation在内部管理处理状态,因此它总是直到自己现在处在栈的什么位置。


拦截器在触发时能做什么:

  1. 做一些预处理。在这个阶段拦截器可以用来准备、过滤、改变或者操作任何可以访问的重要数据。这些数据包括所有与当前请求相关的关键对象和数据,也包括Action。
  2. 通过调用invoke()方法将控制转移给后续的拦截器,直到Action。或者通过返回一个控制字符串中断执行。在这个阶段,如果拦截器决定请求不应该继续,他可以不调用ActionInvocation实例上的invoke()方法,而是直接返回一个控制字符串。通过这种方式可以停止后续的执行,并且决定哪个结果被呈现。
  3. 做一些后加工。在这个阶段,任何一个返回的拦截器可以修改可以访问的对象的数据作为后加工,但是此时结果已经确定了。

4.3  研究内建的Struts 2拦截器
4.3.1 工具拦截器

  • timer拦截器。记录执行花费的时间
  • logger拦截器。提供了一个简单的日志记录机制

4.3.2  数据转移拦截器

  • params拦截器(defaultStack)。params拦截器不知道这些数据最终会去哪里,它只是把数据转移到ValueStack上发现的第一个匹配的属性上。
  • static-params拦截器(defaultStack)。它也将参数转移到ValueStack公开的属性上,不同的是参数的来源。这个拦截器转移的参数定义在声明性架构的Action元素中。
  • autowiring拦截器。这个拦截器为使用Spring管理应用程序资源提供了一个集成点。
  • servlet-config拦截器(defaultStack)。该拦截器提供了一种将来源于Servlet API的各种对象注入到Action的简洁方法。这个拦截器通过将各种对象设置到Action必须实现的接口公开的设置方法的方式工作。每个接口包含一个方法——当前资源的设置方法。不同的接口用于获取与Servlet环境相关的不同对象。 

    ServletContextAware设置ServletContext

    ServletRequestAware设置HttpServletRequest

    ServletResponseAware设置HttpServletResponse

    ParameterAware设置Map类型的请求参数

    RequestAware设置Map类型的请求属性

    SessionAware设置Map类型的会话属性

    ApplicationAware设置Map类型的应用程序领域属性

    PrincipalAware设置Principal对象(安全相关)

  • fileUpload拦截器(defaultStack)。该拦截器将文件和元数据从多重请求转换为常规的请求参数。

4.3.3  工作流拦截器

  • workflow拦截器(defaultStack)
  • validation拦截器(defaultStack),提供了声明性的方式验证你的数据。
  • prepare拦截器(defaultStack),提供了一种向Action追加额外的工作流处理的通用入口点。
  • modelDriven拦截器(defaultStack)

4.3.4 其他拦截器

  • exception拦截器(defaultStack)。它出现在defaultStack中第一位,也应在在任何你创建的自定义拦截器栈中出现在第一位。当exception拦截器在后加工阶段处理时,它会捕获被抛出的任何异常,并且把结它映射到一个结果页面。在将控制转交给结果之前,exception拦截器会创建一个ExceptionHolder对象,并且把它放在ValueStack的最顶端。ExceptionHolder是一个异常的包装器,它把跟踪栈和异常作为JavaBean属性公开出来,可以在错误页面通过标签访问这些属性。
  • token拦截器和token-session拦截器。可以作为避免表单重复提交系统的一部分。
  • scoped-modelDriven拦截器(defaultStack)。这个拦截器为Action的模型对象提供跨请求的向导式的持久性。
  • execAndWait拦截器。当一个请求需要执行很长时间时,最好能给用户一些反馈。

4.4  声明拦截器
此时XML是声明拦截器的唯一选择。注解机制现在还不支持声明拦截器。
只要Action声明了自己的拦截器,它就失去了自动的默认值。


4.5  构建自定义拦截器
拦截器实例在Action之间共享。虽然每个请求都会创建动作的一个新实例,但是拦截器会重用。拦截器是无状态的,不要在拦截器中存储与当前正在处理的请求相关的数据。

 

  • 大小: 27.2 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics