This page last changed on Sep 16, 2004 by vitorsouza.

第五课: 截取器(Interceptors)

截取器可以将任意代码包含在调用栈中活动执行前或之后, 它可以极大的简化代码同时提供增强代码复用能力. XWork和WebWork的许多特性多作为截取器实现, 可以和你自己的拦截器一起通过外部配置任意指定顺序并配置在任意活动上.

换言之, 当你访问一个*.action的地址, WebWork的 class="monospaced">ServletDispatcher执行一个活动调用. 在活动执行前, 这一调用可以被另一个对象截取, 这个对象就称为截取器. 要想在一个活动执行之前(或之后)设置一个截取器, 只需正确配置xwork.xml, 如下例所示, 借用自4.1.1:

4.1.1中的截取器配置:

<action name="formProcessing" class="lesson04_01_01.FormProcessingAction"> 
	<result name="input" type="dispatcher">ex01-index.jsp</result> 
	<result name="success" type="dispatcher">ex01-success.jsp</result> 
	<interceptor-ref name="validationWorkflowStack" /> 
</action>

如你所见, 4.1.1中的活动formProcessing使用了validationWorkflowStack. 它是一个截取器栈, 栈中按照截取器的执行顺序进行组织. 这个截取器栈定义在webwork-default.xml, 这样我们需要做的就是在活动配置的<interceptor-ref />或包配置中的<default-interceptor-ref />中声明, 就像第三课的第一个例一样:

3.1中的截取器配置:

<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" 
"http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>
	<!-- Include webwork defaults (from WebWork JAR). -->
	<include file="webwork-default.xml" />
	
	<!-- Configuration for the default package. -->
	<package name="default" extends="webwork-default">
		<!-- Default interceptor stack. --> 
		<default-interceptor-ref name="defaultStack" /> 
		
		<!-- Action: Lesson 03: HelloWebWorldAction. --> 
		<action name="helloWebWorld" class="lesson03.HelloWebWorldAction"> 
			<result name="success" type="dispatcher">ex01-success.jsp</result> 
		</action> 
	</package>
</xwork>

让我们先看看它是如何工作的:

  1. 创建一个截取器类, 它实现了com.opensymphony.xwork.interceptor.Interceptor接口 (包含在xwork-1.0.jar中);
  2. 在XML配置文件(xwork.xml)中使用嵌套在<interceptors />中的元素<interceptor />声明该类;
  3. 使用<interceptor-stack />元素(可选的)创建截取器栈;
  4. 使用<interceptor-ref /><default-interceptor-ref />决定活动使用那些截取器. 这个样板定义了该截取器被用于一个指定的活动, 而之后指定缺省截取器栈用于那些没有指定<interceptor-ref />的活动.

深入观察webwork-default.xml我们可以发现这是怎样做到的:

webwork-default.xml:

<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" 
"http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>
	<package name="webwork-default">
		<result-types>
			<result-type name="dispatcher" default="true"
				class="com.opensymphony.webwork.dispatcher.ServletDispatcherResult"/>
			<result-type name="redirect" 
				class="com.opensymphony.webwork.dispatcher.ServletRedirectResult"/>
			<result-type name="velocity" 
				class="com.opensymphony.webwork.dispatcher.VelocityResult"/>
			<result-type name="chain" 
				class="com.opensymphony.xwork.ActionChainResult"/>
			<result-type name="xslt" 
				class="com.opensymphony.webwork.views.xslt.XSLTResult"/>
		</result-types>
	
		<interceptors>
			<interceptor name="timer" 
				class="com.opensymphony.xwork.interceptor.TimerInterceptor"/>
			<interceptor name="logger" 
				class="com.opensymphony.xwork.interceptor.LoggingInterceptor"/>
			<interceptor name="chain" 
				class="com.opensymphony.xwork.interceptor.ChainingInterceptor"/>
			<interceptor name="static-params" 
				class="com.opensymphony.xwork.interceptor.StaticParametersInterceptor"/>
			<interceptor name="params" 
				class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/>
			<interceptor name="model-driven" 
				class="com.opensymphony.xwork.interceptor.ModelDrivenInterceptor"/>
			<interceptor name="component" 
				class="com.opensymphony.xwork.interceptor.component.ComponentInterceptor"/>
			<interceptor name="token" 
				class="com.opensymphony.webwork.interceptor.TokenInterceptor"/>
			<interceptor name="token-session" 
				class="com.opensymphony.webwork.interceptor.TokenSessionStoreInterceptor"/>
			<interceptor name="validation" 
				class="com.opensymphony.xwork.validator.ValidationInterceptor"/>
			<interceptor name="workflow" 
				class="com.opensymphony.xwork.interceptor.DefaultWorkflowInterceptor"/>
			<interceptor name="servlet-config" 
				class="com.opensymphony.webwork.interceptor.ServletConfigInterceptor"/>
			<interceptor name="prepare" 
				class="com.opensymphony.xwork.interceptor.PrepareInterceptor"/>
			<interceptor name="conversionError" 
				class="com.opensymphony.webwork.interceptor.WebWorkConversionErrorInterceptor"/>
			<interceptor-stack name="defaultStack">
				<interceptor-ref name="static-params"/>
				<interceptor-ref name="params"/>
				<interceptor-ref name="conversionError"/>
			</interceptor-stack>
			<interceptor-stack name="validationWorkflowStack">
				<interceptor-ref name="defaultStack"/>
				<interceptor-ref name="validation"/>
				<interceptor-ref name="workflow"/>
			</interceptor-stack>
		</interceptors>
	</package>
</xwork>

由于在xwork.xml中包含了webwork-default.xml, 上述全部截取器和战斗可以用于我们的活动. 下面列出这些截取器的功能:
  • timer: 对活动(包含嵌套的截取器和视图)执行进行计时;
  • logger: 纪录将要执行的活动;
  • chain: 使得前一个活动的属性在当前活动中可用. 用于实现活动链(action chaining) (参阅: 结果类型);
  • static-params: 将xwork.xml中定义的参数设置到活动中. 相当于包含在<action />标签中的<param />标签;
  • params: 将请求(POST和GET)的参数设置到活动类中. 在第三课中有这样的例子;
  • model-driven: 如果活动实现ModelDriven, 将getModel()的结果压入值栈;
  • component: 激活注册的组件并允许活动使用. (参见: [IoC与组件]);
  • token: 在活动中检查合法令牌(token), 防止表单的重复提交;
  • token-session: 同上, 但是在接到非法令牌时将提交的数据保存在session中;
  • validation: 使用定义在{Action}-validation.xml中的校验器进行校验(参见: 校验). 在4.1.1中有这样的例子;
  • workflow: 调用活动类中的validate方法. 如果活动产生了错误将返回到输入视图. 应当与校验截取器一起使用(参见: 校验);
  • servlet-config: 提供访问HttpServletRequestHttpServletResponse的途径(使用前请三思, 因为它使你的代码与Servlet接口绑定在一起);
  • prepare: 允许在参数设置之前通过代码访问活动.;
  • conversionError: 此处需要帮助.

构建自己的截取器

如果上述截取器都不符合你的特殊需要, 你需要实现自己的截取器. 幸运的是, 这是一个非常容易完成的任务. 假定我们需要一个截取器根据不同的时段(早晨, 下午或晚上)在session中放入对应的问候语. 下文展示了如何实现它:

GreetingInterceptor.java:

package lesson05;

import java.util.Calendar;
import com.opensymphony.xwork.interceptor.Interceptor;
import com.opensymphony.xwork.ActionInvocation;

public class GreetingInterceptor implements Interceptor {
	public void init() { }
	public void destroy() { }
	public String intercept(ActionInvocation invocation) throws Exception {
		Calendar calendar = Calendar.getInstance();
		int hour = calendar.get(Calendar.HOUR_OF_DAY);
		String greeting = (hour < 6) ? "Good evening" : 
			((hour < 12) ? "Good morning": 
			((hour < 18) ? "Good afternoon": "Good evening"));

		invocation.getInvocationContext().getSession().put("greeting", greeting);

		String result = invocation.invoke();

		return result;
	}
}

xwork.xml:

<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" 
"http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>
	<!-- Include webwork defaults (from WebWork JAR). -->
	<include file="webwork-default.xml" />
	
	<!-- Configuration for the default package. -->
	<package name="default" extends="webwork-default">
		<interceptors> 
			<interceptor name="greeting" class="section02.lesson05.GreetingInterceptor" /> 
		</interceptors> 
		
		<!-- Action: Lesson 5: GreetingInterceptor. --> 
		<action name="greetingAction" class="lesson05.GreetingAction"> 
			<result name="success" type="velocity">ex01-result.vm</result> 
			<interceptor-ref name="greeting" /> 
		</action> 
	</package>
</xwork>

GreetingAction.java:

package lesson05;

import com.opensymphony.xwork.ActionSupport;

public class GreetingAction extends ActionSupport {
	public String execute() throws Exception {
		return SUCCESS;
	}
}

ex01-result.vm:

<html>
<head>
<title>WebWork Tutorial - Lesson 5 - Example 1</title>
</head>
<body>

#set ($ses = $req.getSession())
<p><b>${ses.getAttribute('greeting')}!</b></p>

</body>
</html>

先来看一下截取器类. 和之前介绍的一样, 截取器必须实现com.opensymphony.xwork.interceptor.Interceptor的方法: init(), 将在初始化时被调用, destroy(), 在销毁时被调用, 最为重要, intercept(ActionInvocation invocation), 该方法是加入工作代码的地方.

注意, 我们的截取器返回invocation.invoke()的结果, 该方法将执行栈中下一个截取器, 如果本截取器是栈中的最后一个, 将执行活动. 这意味着截取器有短路(short-circuiting)活动调用的能力, 可以根本不执行活动直接返回一个String结果! 这种用法需要谨慎使用.

截取器的另一个功能是在活动执行后执行代码. 为此, 只需将代码放在invocation.invoke()调用之后. WebWork提供一个已经实现下列行为的抽象类: com.opensymphony.xwork.interceptor.AroundInterceptor. 只须继承(extend)该类并实现方法before(ActionInvocation invocation)after(ActionInvocation dispatcher, String result).

xwork.xml中关于活动类和结果页面的配置内容简洁明了不需要进一步解释.
试试这个例子!


上一课 | 指南结束
Document generated by Confluence on Dec 14, 2004 16:36