第四章 使用拦截器追加工作流
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在内部管理处理状态,因此它总是直到自己现在处在栈的什么位置。
拦截器在触发时能做什么:
- 做一些预处理。在这个阶段拦截器可以用来准备、过滤、改变或者操作任何可以访问的重要数据。这些数据包括所有与当前请求相关的关键对象和数据,也包括Action。
- 通过调用invoke()方法将控制转移给后续的拦截器,直到Action。或者通过返回一个控制字符串中断执行。在这个阶段,如果拦截器决定请求不应该继续,他可以不调用ActionInvocation实例上的invoke()方法,而是直接返回一个控制字符串。通过这种方式可以停止后续的执行,并且决定哪个结果被呈现。
- 做一些后加工。在这个阶段,任何一个返回的拦截器可以修改可以访问的对象的数据作为后加工,但是此时结果已经确定了。
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之间共享。虽然每个请求都会创建动作的一个新实例,但是拦截器会重用。拦截器是无状态的,不要在拦截器中存储与当前正在处理的请求相关的数据。
相关推荐
Struts2的拦截器——Struts2拦截器的基础知识
第二部分 核心概念:动作、拦截器和类型转换 第3章 使用Struts 2动作 36 3.1 Struts 2动作简介 36 3.2 打包动作 39 3.2.1 Struts 2公文包示例应用程序 39 3.2.2 组织你的包 39 3.2.3 使用struts-default包中的组件 ...
Struts2拦截器(Interceptor) Struts2拦截器(Interceptor)
Struts 2实战 struts2 in Action
Struts拦截器的案例,没有使用Hibernate,使用的是JDBC,该代码的核心实现是拦截器,至于数据库可根据个人需求进行修改。
NULL 博文链接:https://wang4717.iteye.com/blog/796941
Struts2拦截器的实现原理(csdn)————程序
Struts2 in action 中文版
本资源包含两个学习Struts的资料: 1.struts2基础.chm 2.Struts2+技术内幕——深入解析Struts2架构设计与实现原理.pdf 欢迎有兴趣的童鞋下载学习。
struts2学习笔记,非本人所写,但有学习的价值,总结的很好,分享一个!
struts2 in action 源码
在struts1中使用拦截器——----------------------------------------------------saif-0.1.jar
Struts2inAction 英文版 非常实用的一本Struts教材
Struts 2实战,Struts 2 in action 的中文版,pdf格式,讲的是Struts 2。此书是Struts 2的一本很经典的书。花几块钱在网上买的。。。。
旺旺教师————Struts2旺旺教师————Struts2旺旺教师————Struts2旺旺教师————Struts2
Struts2 In Action中文版,完整PDF。
学习Struts2第三天笔记
然后厘清了Web开发中极易混淆的一些重要概念,以及Struts2的核心技术、宏观视图、微观元素、配置元素等,提纲挈领地对Struts2进行了多角度的讲解。核心技术篇首先分析了Struts2中多种具有代表性的设计模式,然后对...
Struts2 in action 中文版.zip 经典的Struts2图书