四、 开发自己的拦截器
Struts 2 框架提供了许多拦截器,这些内建拦截器实现了 struts 2 的大部分功能。但还有一些系统逻辑相关的通用功能,则需要通过自定义拦截器来实现,比如我们可以开放自己的拦截器来完成权限控制,日志记录等。Struts 2 的拦截器系统是如此的简单、易用。
4.1 实现拦截器类
如果用户要开发自己的拦截器,应该实现 com.opensymphony.xwork2.interceptor.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
* {@link #intercept(com.opensymphony.xwork2.ActionInvocation) 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 {@link 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 {@link ActionInvocation#invoke()}, or from the interceptor itself.
* @throws Exception any system-level error, as defined in {@link com.opensymphony.xwork2.Action#execute()}.
*/
String intercept(ActionInvocation invocation) throws Exception;
}
上面接口包含 3 个方法:
● init() : 在该拦截器被实例化之后,在该拦截器执行拦截之前,系统将回调该方法。对于每个拦截器而言,其 init() 方法只能执行一次。因此,该方法的方法体主要用于初始化资源,例如数据库连接等。
● destroy() : 在拦截器实例被销毁之前,系统将回调该拦截器的 destroy 方法,一般用于销毁在 init() 方法里打开的资源
● intercept(ActionInvocation invocation) : 该方法是用户需要实现的拦截动作。就像 Action 的 execute() 一样,intercept 方法会返回一个字符串作为逻辑视图。如果该方法直接返回一个字符串,系统将会跳转到该逻辑视图对应的实际视图资源,不会调用被拦截的 Action 。 该方法的 ActionInvocation 参数包含了被拦截的 Action 的引用,可以通过调用该参数的 invoke 方法,将控制权转给下一个拦截器,或者转给 Action 的 execute() 方法
除此之外,Struts 2 还提供了一个 AbstractInterceptor 类 ,该类提供了一个 init 和 destroy 方法的空实现,如果拦截器不需要打开资源,则可以无需实现这 2 个方法。可见,用继承 AbstractInterceptor 类来实现自定义拦截器会更加简单:
package com.opensymphony.xwork2.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
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;
}
下面实现了一个简单的拦截器:
SimpleInterceptor
package js.a;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
import java.util.*;
public class SimpleInterceptor extends AbstractInterceptor {
// 简单拦截器的名字
private String name;
// 为该简单拦截器设置名字的setter方法
public void setName(String name) {
this.name = name;
}
public String intercept(ActionInvocation invocation) throws Exception {
// 取得被拦截的Action实例
LoginAction action = (LoginAction) invocation.getAction();
// 打印执行开始的实现
System.out.println(name + " 拦截器的动作---------" + "开始执行登录Action的时间为:"
+ new Date());
// 取得开始执行Action的时间
long start = System.currentTimeMillis();
// 执行该拦截器的后一个拦截器
// 如果该拦截器后没有其他拦截器,则直接执行Action的execute方法
String result = invocation.invoke();
// 打印执行结束的时间
System.out.println(name + " 拦截器的动作---------" + "执行完登录Action的时间为:"
+ new Date());
long end = System.currentTimeMillis();
System.out.println(name + " 拦截器的动作---------" + "执行完该Action的事件为"
+ (end - start) + "毫秒");
return result;
}
}
ActionInvocation 参数可以获得被拦截的 Action 实例,一旦取得了 Action 实例,几乎获得了全部的控制权: 可以实现将 HTTP 请求中的参数解析出来,设置成 Action 的属性(这是系统 params 拦截器干的事情);也可以直接将 HTTP 请求中的 HttpServletRequest 实例和 HttpServleResponse 实例传给 Action (这是 servletConfig 拦截器干的事情),也可以实现应用相关的逻辑
LoginAction
package js.a;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
public String execute() throws Exception {
System.out.println("进入execute方法执行体..........");
Thread.sleep(1500);
if (getUsername().equals("crazyit") && getPassword().equals("leegang")) {
return SUCCESS;
} else {
return ERROR;
}
}
}
struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.2.dtd"> <struts> <constant name="struts.custom.i18n.resources" value="messageResource"/> <constant name="struts.i18n.encoding" value="GBK"/> <include file="struts_1.xml"/> </struts>
struts_1.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.2.dtd"> <struts> <constant name="struts.custom.i18n.resources" value="messageResource"/> <constant name="struts.i18n.encoding" value="GBK"/> <package name="js.a" extends="struts-default" namespace="/10"> <!-- 应用所需使用的拦截器都在该元素下配置 --> <interceptors> <!-- 配置mysimple拦截器 --> <interceptor name="mysimple" class="js.a.SimpleInterceptor"> <!-- 为拦截器指定参数值 --> <param name="name">简单拦截器</param> </interceptor> </interceptors> <action name="login" class="js.a.LoginAction"> <result name="error">/10/error.jsp</result> <result name="success">/10/welcome.jsp</result> <!-- 拦截器一般配置在result元素之后! --> <!-- 配置系统的默认拦截器 --> <interceptor-ref name="defaultStack"/> <!-- 应用自定义的mysimple拦截器 --> <interceptor-ref name="mysimple"> <param name="name">改名后的拦截器</param> </interceptor-ref> </action> <action name=""> <result>.</result> </action> </package> </struts>
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>struts2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
login.jsp
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>登录页面</title>
</head>
<body>
<form action="login.action" method="post">
<table align="center">
<caption><h3>用户登录</h3></caption>
<tr>
<td>用户名:<input type="text" name="username"/></td>
</tr>
<tr>
<td>密码:<input type="text" name="password"/></td>
</tr>
<tr align="center">
<td><input type="submit" value="登录"/>
<input type="reset" value="重填" /></td>
</tr>
</table>
</form>
</body>
</html>
error.jsp
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>错误页面</title>
</head>
<body>
您不能登录!
</body>
</html>
welcome.jsp
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>成功页面</title>
</head>
<body>
您已经登录!
</body>
</html>
index.jsp
<s:a href="" οnclick="newWin('10/login.jsp');" cssStyle="cursor: hand;">login.jsp</s:a>
4.2 使用拦截器
以下所有代码都基于 4.1
使用拦截器需要 2 个步骤:
① 通过 <interceptor ../> 元素来定义拦截器
② 通过 <interceptor-ref ../> 元素来使用拦截器
看 4.1 中的 struts_1.xml
注意: 如果为 Action 指定了一个拦截器,则系统默认的拦截器栈将会失去作用。为了继续使用默认拦截器,所以上面配置文件中手动应用了默认拦截器。
4.3 拦截器和 Struts 2 插件的关系
扩展 Struts 2 的重要方式 : 插件,通过使用插件可以极好地扩充 Struts 2 的功能,在 Struts 2 项目的目录里,可以看到大量名为 struts2-xxx-plugin-2.2.jar 的文件,这些文件都是 Struts 2 的插件文件
Struts 2 的拦截器可用于完成各种细粒度的通用 “小功能” ,当我们需要为 Struts 2 扩展新功能时,也可能需要增加新的通用功能,这时候就需要开发自己的拦截器
当开发者开发了自己的拦截器之后,还必须配置拦截器,通常我们不可能修改 Struts 2 的 struts-default.xml ,而通用功能的拦截器也不应该在应用相关的 struts.xml 文件中配置。这就需要在 Struts 2 插件的 struts-plugin.xml 文件中配置拦截器了
例如: struts2-rest-plugin-2.2.1.jar 文件,包含了一个 struts-plugin.xml 文件:
struts-plugin.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <bean type="com.opensymphony.xwork2.ActionProxyFactory" name="rest" class="org.apache.struts2.rest.RestActionProxyFactory" /> <bean type="org.apache.struts2.dispatcher.mapper.ActionMapper" name="rest" class="org.apache.struts2.rest.RestActionMapper" /> <bean type="org.apache.struts2.rest.ContentTypeHandlerManager" class="org.apache.struts2.rest.DefaultContentTypeHandlerManager" /> <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="xml" class="org.apache.struts2.rest.handler.XStreamHandler" /> <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="json" class="org.apache.struts2.rest.handler.JsonLibHandler" /> <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="html" class="org.apache.struts2.rest.handler.HtmlHandler" /> <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="x-www-form-urlencoded" class="org.apache.struts2.rest.handler.FormUrlEncodedHandler" /> <bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="multipart/form-data" class="org.apache.struts2.rest.handler.MultipartFormDataHandler" /> <constant name="struts.actionProxyFactory" value="rest" /> <constant name="struts.rest.defaultExtension" value="xhtml" /> <constant name="struts.mapper.class" value="rest" /> <constant name="struts.mapper.idParameterName" value="id" /> <constant name="struts.action.extension" value="xhtml,,xml,json" /> <package name="rest-default" extends="struts-default"> <result-types> <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"> <param name="statusCode">303</param> </result-type> <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"> <param name="statusCode">303</param> </result-type> </result-types> <interceptors> <!-- 配置 Struts 2 REST 插件的拦截器 --> <interceptor name="rest" class="org.apache.struts2.rest.ContentTypeInterceptor"/> <interceptor name="restWorkflow" class="org.apache.struts2.rest.RestWorkflowInterceptor"/> <interceptor name="messages" class="org.apache.struts2.interceptor.MessageStoreInterceptor" /> <interceptor-stack name="restDefaultStack"> <interceptor-ref name="exception"/> <interceptor-ref name="alias"/> <interceptor-ref name="servletConfig"/> <interceptor-ref name="messages"> <param name="operationMode">AUTOMATIC</param> </interceptor-ref> <interceptor-ref name="prepare"/> <interceptor-ref name="i18n"/> <interceptor-ref name="chain"/> <interceptor-ref name="debugging"/> <interceptor-ref name="profiling"/> <interceptor-ref name="actionMappingParams"/> <interceptor-ref name="scopedModelDriven"/> <interceptor-ref name="modelDriven"> <param name="refreshModelBeforeResult">true</param> </interceptor-ref> <interceptor-ref name="fileUpload"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="staticParams"/> <interceptor-ref name="params"> <param name="excludeParams">dojo\..*</param> </interceptor-ref> <interceptor-ref name="rest" /> <interceptor-ref name="conversionError"/> <interceptor-ref name="validation"> <param name="excludeMethods">input,back,cancel,browse,index,show,edit,editNew</param> </interceptor-ref> <interceptor-ref name="restWorkflow"> <param name="excludeMethods">input,back,cancel,browse,index,show,edit,editNew</param> </interceptor-ref> </interceptor-stack> </interceptors> <!-- 配置默认的拦截器栈引用 --> <default-interceptor-ref name="restDefaultStack"/> <default-class-ref class="org.apache.struts2.rest.RestActionSupport"/> </package> </struts>
通过上面配置文件可以看出 : 当需要为 Struts 2 扩展新功能时, Struts 2 插件是外在组织形式,而新增的拦截器则用于完成通用的核心功能,而 Struts 2 插件则负责组织这些新增的拦截器
五、 深入拦截器编程
拦截器的配置离不开 <interceptor .../> <interceptor-ref .../> <interceptor-stack .../> ,但对于拦截器的深入编程方面,还有一些值得注意的地方
5.1 拦截方法的拦截器
在默认情况下,如果为某个 Action 定义了拦截器,则这个拦截器会拦截该 Action 内的所有方法。但在某些情况下,我们不想拦截所有方法,只需要拦截指定方法,此时,就需要使用 Struts 2 拦截器的方法过滤特性
为了实现方法过滤特性, 需要使用一个 MethodFilterInterceptor 类,该类是 AbstractInterceptor 类的子类。如果用户需要自己实现的拦截器支持方法过滤特性,则应该继承 MethodFilterInterceptor 类
MethodFilterInterceptor 类重写了 AbstractInterceptor 类的 intercept(ActionInvocation invocation) ,但提供了一个 doIntercept(ActionInvocation invocation) 抽象方法。 MethodFilterInterceptor 的 intercept 已经实现了对 Action 的拦截行为(只实现了方法过滤的逻辑),但真正的拦截逻辑还需要开发者提供,也就是通过回调 doIntercept 方法实现。 因此用户应该重写 doIntercept(ActionInvocation invocation)
例如 :
MyFilterInterceptor
package js.a;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import java.util.*;
//方法过滤的拦截器,应该继承MethodFilterInterceptor抽象类
public class MyFilterInterceptor extends MethodFilterInterceptor {
// 简单拦截器的名字
private String name;
// 为该简单拦截器设置名字的setter方法
public void setName(String name) {
this.name = name;
}
// 重写doIntercept方法,实现对Action的拦截逻辑
public String doIntercept(ActionInvocation invocation) throws Exception {
// 取得被拦截的Action实例
LoginActionMy action = (LoginActionMy) invocation.getAction();
// 打印执行开始的时间
System.out.println(name + " 拦截器的动作---------" + "开始执行登录Action的时间为:"
+ new Date());
// 取得开始执行Action的时间
long start = System.currentTimeMillis();
// 执行该拦截器的后一个拦截器,或者直接指定Action的execute方法
String result = invocation.invoke();
// 打印执行结束的时间
System.out.println(name + " 拦截器的动作---------" + "执行完登录Action的时间为:"
+ new Date());
long end = System.currentTimeMillis();
// 打印执行该Action所花费的时间
System.out.println(name + " 拦截器的动作---------" + "执行完该Action的事件为"
+ (end - start) + "毫秒");
return result;
}
}
方法过滤的拦截器与实现普通拦截器并没有太大区别,只要注意 2 个地方 : 实现方法过滤的拦截器需要继承 MethodFilterInterceptor 抽象类,并且重写 doIntercept 方法定义对 Action 的拦截逻辑
在 MethodFilterInterceptor 抽象类中,额外增加 2 个方法:
● public void setExcludeMethods(String excludeMethods) : 排除需要过滤的方法 -----> 设置方法 “黑名单” ,所有在 excludMethods 字符串中列出的方法都不会被拦截
● public void setIncludeMethods(String includeMethods) : 设置需要过滤的方法 -----> 设置方法 “白名单” ,所有在 includeMehods 字符串中列出的方法都会被拦截
注意 : 如果一个方法同时在 excludeMethods 和 includeMethods 中列出,则该方法会被拦截
因为 MethodFilterInterceptor 类包含如上 2 个方法,则该拦截器的子类也会获得这 2 个方法,可以在 struts.xml 中指定需要被拦截或者不需要被拦截的方法
LoginActionMy
package js.a;
import com.opensymphony.xwork2.ActionSupport;
public class LoginActionMy extends ActionSupport {
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword() {
return password;
}
public String execute() throws Exception {
System.out.println("进入execute方法执行体..........");
Thread.sleep(1500);
if (getUsername().equals("crazyit") && getPassword().equals("leegang")) {
return SUCCESS;
} else {
return ERROR;
}
}
}
struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.2.dtd"> <struts> <constant name="struts.custom.i18n.resources" value="globalMessages"/> <constant name="struts.i18n.encoding" value="GBK"/> <package name="lee" extends="struts-default"> <interceptors> <interceptor name="myfilter" class="js.a.MyFilterInterceptor"> <param name="name">方法过滤拦截器</param> </interceptor> </interceptors> <action name="loginMy" class="js.a.LoginActionMy"> <result name="error">/10/erroMy.jsp</result> <result name="success">/10/welcomeMy.jsp</result> <!-- 拦截器一般配置在result元素之后! --> <interceptor-ref name="defaultStack"/> <interceptor-ref name="myfilter"> <param name="name">改名后的方法过滤拦截器</param> <!-- 不被拦截的方法 --> <param name="excludeMethods">execute,haha</param> <!-- 被拦截的方法 --> <param name="includeMethods">execute</param> </interceptor-ref> </action> <action name=""> <result>.</result> </action> </package> </struts>
同时指定多个方法,可以用 (,)隔开
excludeMethods 参数指定了 execute 和 haha 方法不需要被拦截,又通过 includeMethods 参数指定了 execute 方法需要拦截,两者冲突,以 includeMethods 参数指定的为准,即拦截器会拦截 execute 方法
Struts 2 中提供了这种方法过滤的拦截器有如下几个:
● TokenInterceptor
● TokenSessionStoreInterceptor
● DefaultWorkflowInterceptor
● ValidationInterceptor
loginMy.jsp
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<%@taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>登录页面</title>
</head>
<body>
<form action="loginMy.action" method="post">
<table align="center">
<caption><h3>用户登录</h3></caption>
<tr>
<td>用户名:<input type="text" name="username"/></td>
</tr>
<tr>
<td>密码:<input type="text" name="password"/></td>
</tr>
<tr align="center">
<td><input type="submit" value="登录"/>
<input type="reset" value="重填" /></td>
</tr>
</table>
</form>
</body>
</html>
welcomeMy.jsp
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>成功页面</title>
</head>
<body>
您已经登录!
</body>
</html>
erroMy.jsp
<%@ page contentType="text/html; charset=GBK" language="java" errorPage="" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>错误页面</title>
</head>
<body>
您不能登录!
</body>
</html>
<s:a href="" οnclick="newWin('10/loginMy.jsp');" cssStyle="cursor: hand;">loginMy.jsp</s:a>
5.2 拦截器的执行顺序
以下代码基于 4.1
随着系统中配置拦截器的顺序的不同,系统中执行拦截器的顺序也不一样。通常认为: 先配置的拦截器,会先获得执行的机会,但实际的情况则有些出入
struts.xml
<?xml version="1.0" encoding="GBK"?> <!-- 指定Struts 2的配置文件的DTD信息 --> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <struts> <!-- 通过常量配置Struts 2的国际化资源信息 --> <constant name="struts.custom.i18n.resources" value="globalMessages"/> <!-- 通过常量配置Struts 2所使用的解码集--> <constant name="struts.i18n.encoding" value="GBK"/> <!-- 配置本系统所使用的包 --> <package name="lee" extends="struts-default"> <!-- 配置应用所使用的拦截器 --> <interceptors> <!-- 配置mysimple拦截器 --> <interceptor name="mysimple" class="lee.SimpleInterceptor"> <!-- 为拦截器指定参数值 --> <param name="name">简单拦截器</param> </interceptor> </interceptors> <!-- 配置处理用户请求的Action --> <action name="login" class="lee.LoginAction"> <!-- 配置结果映射 --> <result name="error">/error.jsp</result> <result name="success">/welcome.jsp</result> <!-- 拦截器一般配置在result元素之后! --> <interceptor-ref name="defaultStack"/> <!-- 下面的配置片段两次引用同一个拦截器 --> <interceptor-ref name="mysimple"> <param name="name">第一个</param> </interceptor-ref> <interceptor-ref name="mysimple"> <param name="name">第二个</param> </interceptor-ref> </action> <action name=""> <result>.</result> </action> </package> </struts>
从运行后台里显示内容可以得出结论: 在 Action 的控制逻辑方法执行之前,位于拦截器链前面的拦截器将先发生作用;在 Action 的控制方法执行之后,位于拦截器链前面的拦截器将后发生作用
5.3 拦截结果的监听器
以下代码基于 5.1
为了精确定义在 execute() 执行结束后,在处理物理资源转向之前的动作,需要使用拦截结果监听器,这个监听器是通过手动注册在拦截内部的 。
实现拦截结果监听器,必须实现 PreResultListener 接口:
MyPreResultListener.java
package js.a;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.PreResultListener;
public class MyPreResultListener implements PreResultListener {
// 定义在处理Result之前的行为
public void beforeResult(ActionInvocation invocation, String resultCode) {
// 打印出执行结果
System.out.println("返回的逻辑视图为:" + resultCode);
}
}
resultCode 参数就是被拦截 Action 的 execute() 返回值。
虽然 beforeResult() 也获得 ActionInvocation 参数,但通过这个参数来控制 Action 已经没有用了,因为 execute() 已经执行结束
拦截器代码 BeforeResultInterceptor.java :
package js.a;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class BeforeResultInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
// 将一个拦截结果的监听器注册给该拦截器
invocation.addPreResultListener(new MyPreResultListener());
System.out.println("execute方法执行之前的拦截...");
// 调用下一个拦截器,或者Action的执行方法
String result = invocation.invoke();
System.out.println("execute方法执行之后的拦截...");
return result;
}
}
结果:
execute方法执行之前的拦截... 进入execute方法执行体.......... 返回的逻辑视图为::error execute方法执行之后的拦截...
上面代码手动注册了一个拦截结果的监听器,该监听器中的 beforeResult() 肯定会在系统处理物理资源转向之前被执行,上面的拦截器也定义了需要在处理物理资源转向之前执行的代码
定义在 MyPreResultListener 类中的 beforeResult() 比 BeforeResultInterceptor 类中的 intercept() 中在 Result 之前执行的动作,更早执行。
struts.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.2.dtd"> <struts> <constant name="struts.custom.i18n.resources" value="messageResource"/> <constant name="struts.i18n.encoding" value="GBK"/> <package name="js.a" extends="struts-default" namespace="/10"> <!-- 应用所需使用的拦截器都在该元素下配置 --> <interceptors> <interceptor name="resultInterceptor" class="js.a.BeforeResultInterceptor"/> </interceptors> <action name="loginA" class="js.a.LoginActionMy"> <result name="error">/10/error.jsp</result> <result name="success">/10/welcome.jsp</result> <!-- 拦截器一般配置在result元素之后! --> <interceptor-ref name="defaultStack"/> <interceptor-ref name="resultInterceptor"/> </action> <action name=""> <result>.</result> </action> </package> </struts>
LoginActionMy.java 在 5.1 中
error.jsp,welcome.jsp 在 4.1 中
loginMy1.jsp
<body>
<form action="loginA.action" method="post">
<table align="center">
<caption><h3>用户登录</h3></caption>
<tr>
<td>用户名:<input type="text" name="username"/></td>
</tr>
<tr>
<td>密码:<input type="text" name="password"/></td>
</tr>
<tr align="center">
<td><input type="submit" value="登录"/>
<input type="reset" value="重填" /></td>
</tr>
</table>
</form>
</body>
<s:a href="" οnclick="newWin('10/loginMy1.jsp');" cssStyle="cursor: hand;">loginMy1.jsp</s:a>
5.4 覆盖拦截器栈里特定拦截器的参数
以下代码基于 5.1
拦截器栈中有多个拦截器,如果要覆盖该拦截器栈中某个拦截器的指定参数,需要在配置使用拦截器栈的地方 <interceptor-ref /> 元素中使用 <param/> 元素来传入参数,在 <param /> 中指定参数名时应使用:“拦截器名.参数名” 形式,这样才能让 Struts 2 明白我们想覆盖哪个拦截器的哪个参数
<?xml version="1.0" encoding="GBK"?> <!-- 指定Struts 2的配置文件的DTD信息 --> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd"> <struts> <!-- 通过常量配置Struts 2的国际化资源信息 --> <constant name="struts.custom.i18n.resources" value="globalMessages"/> <!-- 通过常量配置Struts 2所使用的解码集--> <constant name="struts.i18n.encoding" value="GBK"/> <!-- 配置本系统所使用的包 --> <package name="lee" extends="struts-default"> <!-- 配置应用所使用的拦截器 --> <interceptors> <!-- 配置mysimple拦截器 --> <interceptor name="mysimple" class="lee.SimpleInterceptor"> <!-- 为拦截器指定参数值 --> <param name="name">简单拦截器</param> </interceptor> <!-- 配置第二个拦截器 --> <interceptor name="second" class="lee.SecondInterceptor"/> <!-- 配置名为my-stack的拦截器栈 --> <interceptor-stack name="my-stack"> <!-- 配置拦截器栈内的第一个拦截器 --> <interceptor-ref name="mysimple"> <param name="name">第一个</param> </interceptor-ref> <!-- 配置拦截器栈内的第二个拦截器 --> <interceptor-ref name="second"> <param name="name">第二个</param> </interceptor-ref> </interceptor-stack> </interceptors> <!-- 配置处理用户请求的Action --> <action name="login" class="lee.LoginAction"> <!-- 配置结果映射 --> <result name="error">/error.jsp</result> <result name="success">/welcome.jsp</result> <!-- 拦截器一般配置在result元素之后! --> <interceptor-ref name="defaultStack"/> <!-- 应用上面的拦截器栈 --> <interceptor-ref name="my-stack"> <!-- 覆盖指定拦截器的指定参数值 --> <param name="second.name">改名后的拦截器</param> </interceptor-ref> </action> <action name=""> <result>.</result> </action> </package> </struts>
上面配置了一个名为 my-stack 的拦截器栈,并且包括 2 个拦截器,这 2 个拦截器分别引用 mysimple 和 second 拦截器,并且覆盖了 mysimple 拦截器的默认参数
在 login 的 Action 中使用 my-stack 拦截器时,使用了 <param .../> 元素来覆盖了该拦截器栈里 second 拦截器的 name 参数 (second.name )