一、Struts2拦截器的概念
有时候我们想在Action中做一些诸如输入验证、文件上传或者防止重复提交表单的操作,Struts2提供了一种策略,叫做拦截器,当你请求Action的时候,Struts2框架调用这个Action,但是在Action被执行之前或者Action执行之后,拦截器会起作用,这个要取决于配置。这样就能起到比较好的安全性,同时也能达到我们的目的。拦截器是一个很便携的插件,可以根据我们实际的逻辑需求进行增加或者删除,或者自己编程实现。
二、Strtus2拦截器的运行机制
首先我们先来看一下在Struts2里Action的生命周期
(图片来自Apache Struts2官方)
从图中不难看出,当用户发起请求时,由ServletDispatcher(Servlet调度器)实例化一个ActionProxy(Action代理)对象,要想执行这个Action,用户的请求必须突破重重的Interceptor(拦截器),就像在G20峰会时我们去杭州一样,安检重重呐!这样的目的一是为了可以进行一些耗时操作,二是为了安全。当这个Action执行完毕之后,Action会把结果传送出去。
Struts2的拦截器是一个栈结构,所以说,拦截器的安放顺序非常重要,这个安放顺序将决定了你的Action是否能被顺利执行。
在准备执行Action前或者执行后,拦截器(如果我们有明确定义的话),将会起到拦截请求的作用,此时,只有Action里的execute方法会被调用(这里指的是我们自定义拦截器栈的情况,并且没有引入官方的defaultStack拦截器栈。如果我们在拦截器栈中引入了官方的defaultStack的话,将会先执行validate方法,再执行execute方法),业务逻辑也要写在execute方法里,需要注意的是,一些特定的拦截器,比如 Execute and Wait Interceptor(执行并等待拦截器),一旦某个Action被设置了这个拦截器,则这个Action在工作时会独立于主线程,这样会导致我们无法像原来一样使用get方法获取到原来属性被设置的值,会抛出NullPointer Exception(空指针异常),这个需要根据实际情况对其他的拦截器进行设置。因此,Execute and Wait Interceptor必须位于拦截器栈的栈底。例如后面的实例。
在Struts2里官方提供的拦截器有很多,每个拦截器或多或少都有自己的参数,要注意查文档,有些参数不能混用!
三、实例(实现用户登陆时执行耗时操作并在任务完成后进行页面跳转)
在这个例子中,我们将自定义拦截器的栈结构,并且不引入官方的defaultStack拦截器栈
1、知识储备
a、这里要用到三个Struts2的内置拦截器Parameters Interceptor(参数拦截器)、Validation Interceptor(验证拦截器)和 Execute and Wait Interceptor(执行且等待拦截器),根据官方文档,他们的功能如下
Parameters Interceptor : Sets the request parameters onto the Action.
解释:将用户的请求参数(一般是表单)设置进入Action里,也就是调用在某个Action中我们已经定义好的set方法。
Validation Interceptor:This interceptor runs the action through the standard validation framework, which in turn checks the action against any validation rules.
解释:这个验证拦截器会在验证框架启动时验证用户的输入数据,如果我们有定义action-validation.xml(eg.LoginAction-validation.xml),该拦截器将会调用它。需要注意的是,在这个验证拦截器中,默认放行在Action中的validate方法,并且提供excludeMethods和includeMethods属性。这两个属性的意思是:exclude,在英文中的解释是排他的,也就是说在拦截器运行时将忽略这个方法。include与之对应,将在拦截器运行时放行这个方法。
Execute and Wait Interceptor:Executes the Action in the background and then sends the user off to an intermediate waiting page.
解释:这个拦截器会在后台执行耗时操作,并且让用户看到等待界面。需要注意的是,这个拦截器在工作的时候,是独立与主线程的,所以要获取Session数据的时候需要使用到SessionAware方法,本例子没有用到,就不展开叙述。这个拦截器提供threadPriority、delay、delaySleepInterval属性。第一个threadPriority属性故名思意就是线程的优先级,默认值为Thread.NORM_PRIORITY。第二个delay,延迟,也就是说延迟几毫秒向用户展示等待界面,默认是没有设置延迟时间的。第三个delaySleepInterval,这个属性只能与delay属性一并使用,将设置一个毫秒数表示每隔多少毫秒,自动检测后台耗时操作是否完成。在这个拦截器中,如果被检测到后台耗时任务未完成,将返回结果wait。
b、在这个实例中,还将用到ActionError与ActionMessage方法,这两个方法的作用是设置Action执行时的结果信息,他们继承自ActionSupport类。
2、项目的目录结构(由易百教程改编)
It based on this webside
http://www.yiibai.com/struts_2/struts-2-actionerror-actionmessage-example.html
项目结构:
其中*.properties文件可以在新建文件时选则file或者在项目外创建完拖入
LoginAction.properties内容如下
#Welcome messages
welcome.hello = 你好
#error message
username.required = 用户名不可以为空
password.required = 密码不可以为空
global.properties内容如下
#Global messages
global.username = 用户名
global.password = 密码
global.submit = 提交
global.reset = 重置
LoginAction.java文件如下
package org.myweb.action;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport
{
private String user;
private String password;
private boolean isValidate = false;
public void setUser(String user) {
this.user = user;
}
public void setPassword(String password) {
this.password = password;
}
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
public String execute()
{
for(int i = 0 ; i < 1000000 ; i++)
{
System.out.println(i);
}
validate();
if(this.isValidate)
{
return "success";
}
else
{
return "input";
}
}
public void validate()
{
if(getUser().equals("abc") && getPassword().equals("abc"))
{
addActionMessage("OK");
this.isValidate = true;
}
else
{
addActionError("FAILED");
}
}
}
在这个登陆的Action中,设置了execute方法,执行100万次循环,模拟耗时操作,同时,设置了validate方法,用于验证,如果验证成功isValidate属性将变为true,在execute的耗时操作执行完毕之后,将根据这个属性的值返回不同的结果。
struts.xml文件如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN" "http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<constant name="struts.custom.i18n.resources" value="global" />
<package name="user" namespace="/user" extends="struts-default">
<action name="login">
<result>/pages/login.jsp</result>
</action>
<action name="loginAction" class="org.myweb.action.LoginAction">
<interceptor-ref name="params"/>
<interceptor-ref name="validation">
<param name="includeMethods">getUser,getPassword</param>
</interceptor-ref>
<interceptor-ref name="execAndWait">
<param name="delay">1000</param>
<param name="delaySleepInterval">500</param>
</interceptor-ref>
<result name="wait">/pages/wait.jsp</result>
<result name="success">/pages/welcome.jsp</result>
<result name="input">/pages/login.jsp</result>
</action>
</package>
</struts>
我定义了以上提及的三个拦截器,第一层是参数拦截器prams,用于给Action设置用户提交过来的用户名和密码,第二层是验证拦截器validation,并且放行了getUser和getPassword方法,用于用户验证,第三层是执行且等待拦截器,设置为延迟1秒向用户展示等待界面,每隔500毫秒检测耗时操作是否完毕。这就是一个简单的拦截器的Stack结构,真正运行时,将依照这个结构执行。
执行的机制是,先设置参数,再放行validate、getUser、getPassword方法,最后调用execute方法,所有被放行的方法都要通过走execute方法才能被调用!
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Struts2 ActionError & ActionMessage 示例</title>
<style type="text/css">
.errors {
background-color:#FFCCCC;
border:1px solid #CC0000;
width:400px;
margin-bottom:8px;
}
.errors li{
list-style: none;
}
</style>
</head>
<body>
<h1>Struts2 Delay Login</h1>
<s:if test="hasActionErrors()">
<div class="errors">
<s:actionerror/>
</div>
</s:if>
<s:form action="loginAction" method="post">
<s:textfield key="global.user" name="user"/>
<s:password key="global.password" name="password"/>
<s:submit key="global.submit" name="submit"/>
</s:form>
</body>
</html>
wait.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="refresh" content="5;url=<s:url />"/>
<title>My JSP 'wait.jsp' starting page</title>
</head>
<body>
<h2>Please wait until the 1 million loops complete!</h2>
</body>
</html>
welcome.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>Struts2 Delay Login</title>
<style type="text/css">
.welcome {
background-color: #DDFFDD;
border: 1px solid #009900;
width: 200px;
}
.welcome li {
list-style: none;
}
</style>
</head>
<body>
<h1>Struts 2 ActionError & ActionMessage示例</h1>
<s:if test="hasActionMessages()">
<div class="welcome">
<s:actionmessage />
</div>
</s:if>
<h4>
<s:property value="getText('welcome.hello')" />
<s:property value="username" />
</h4>
</body>
</html>
运行效果如下:
MyEclipse后台