开发环境:
Eclipse IDE for Java EE Developers(下载地址)
struts-2.3.1.2(下载地址)
apache-tomcat-6.0.35(下载地址)
效果图:
以下讲解摘录自《Struts2 权威指南》李刚
拦截器体系是Struts2的一个重要组成部分,对于Struts2框架而言,可以将其理解为一个空容器,正是大量的内建拦截器完成了该框架的大部分操作。比如,params拦截器将http请求中的参数解析出来,设置成Action的属性;servlet-config拦截器直接将http请求中的HttpServletRequest实例和HttpServletResponse实例传给Action;fileUpload拦截器则负责解析请求参数中的文件域,并将一个文件域设置成Action的三个属性等等。
对于Struts2的拦截器体系而言,当我们需要使用某个拦截器时,只需要在配置文件中应用该拦截器即可;如果不需要使用该拦截器,也只需要取消在配置文件中应用该拦截器-不管是否应用某个拦截器,对于整个Struts2框架不会有任何影响。这种设计哲学是一种可插拔式的设计,具有非常好的可扩展性。
拦截器可以动态地拦截发送到指定Action的请求,通过拦截器机制,我们可以再Action执行的前后插入某些代码。通过这种方式,就可以把多个Action中需要重复指定的代码提取出来,放在拦截器里定义,从而提供更好的代码重用性。
对于struts2框架的Action而言,它们有一些总是需要完成输入校验,有一些总是需要解析文件上传表单中的文件域,有一些总是需要防止表单的多次提交等等。
在这种需求下,当然可以通过在Action中分别调用不同的方法来实现这些需求,但这种直接调用方法的方式缺陷很明显:Action类直接调用的用所需方法,造成了该Action与特定方法耦合,降低了Action类的复用性。
拦截器通过在配置文件中指定拦截器,从而可以让拦截器方法在目标方法执行之前或者执行之后自动执行,从而完成通用操作的动态插入。在这种策略下,类型转换的处理、数据校验的处理、文件上传的处理、阻止表单多次提交的处理。。。这些不同的通用处理,都被定义成相应的拦截器。如果用户的Action需要使用特定的通用功能,只需要在struts.xml文件制定拦截器引用,可以在Action之前完成这些通用方法。
通过实现Interceptor接口,实现自己的拦截器
Init():在该拦截器被初始化之后,拦截器执行拦截之前,系统将回调该方法。对于每个拦截器而言,该init()方法只执行一次。因此,该方法的方法体主要用于打开一些一次性的资源,例如数据库资源等。
Destroy():该方法与init()方法对应。在拦截器实例被销毁之前,系统将回调拦截器的destroy方法,该方法用于销毁在init()方法打开的资源。
Intercept(ActionInvocation invocation):该方法是用户需要实现的拦截动作。就像Action的execute方法一样,intercept方法会返回一个字符串作为逻辑视图。如果该方法直接返回一个字符串,系统将会跳转到该逻辑视图对应的实际视图资源,不会调用被拦截的Action。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以用过该参数的invoke方法,将控制权转给下一个拦截器或者转给Action的execute方法。
当配置一个包时,可以为其制定默认拦截器。一旦为某个包指定了默认的拦截器,如果该包中的Action没有显示制定拦截器,则默认的拦截器将会起作用。
Struts2的内建拦截器defaultStack(通常不推荐自己为每一个Action分别定义拦截器,而是推荐直接使用系统的defaultStack拦截器栈)
Struts2的struts-default包中指定defaultStack拦截器时默认的拦截器,因此如果用户定义的包继承了struts-default包,则也会将defaultStack拦截器栈作为默认的拦截器栈。这意味着:如果系统中的Action配置没有指定拦截器引用,系统会将defaultStack拦截器自动作用于该Action。
但是,如果为Action指定了一个拦截器,则系统默认的拦截器将会失去作用,为了继续使用默认拦截器,在配置文件中需要动手引入默认拦截器。
Struts2自带的拦截器
拦截器 | 名字 | 说明 |
Alias Interceptor | alias | 在不同请求之间将请求参数在不同名字件转换,请求内容不变 |
Chaining Interceptor | chain | 让前一个Action的属性可以被后一个Action访问,现在和chain类型的result()结合使用。 |
Checkbox Interceptor | checkbox | 添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox。 |
Cookies Interceptor | cookies | 使用配置的name,value来是指cookies |
Conversion Error Interceptor | conversionError | 将错误从ActionContext中添加到Action的属性字段中。 |
Create Session Interceptor | createSession | 自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。 |
Debugging Interceptor | debugging | 提供不同的调试用的页面来展现内部的数据状况。 |
Execute and Wait Interceptor | execAndWait | 在后台执行Action,同时将用户带到一个中间的等待页面。 |
Exception Interceptor | exception | 将异常定位到一个画面 |
File Upload Interceptor | fileUpload | 提供文件上传功能 |
I18n Interceptor | i18n | 记录用户选择的locale |
Logger Interceptor | logger | 输出Action的名字 |
Message Store Interceptor | store | 存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。 |
Model Driven Interceptor | model-driven | 如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。 |
Scoped Model Driven | scoped-model-driven | 如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用Action的setModel方法将其放入Action内部。 |
Parameters Interceptor | params | 将请求中的参数设置到Action中去。 |
Prepare Interceptor | prepare | 如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法。 |
Scope Interceptor | scope | 将Action状态存入session和application的简单方法。 |
Servlet Config Interceptor | servletConfig | 提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问。 |
Static Parameters Interceptor | staticParams | 从struts.xml文件中将中的中的内容设置到对应的Action中。 |
Roles Interceptor | roles | 确定用户是否具有JAAS指定的Role,否则不予执行。 |
Timer Interceptor | timer | 输出Action执行的时间 |
Token Interceptor | token | 通过Token来避免双击 |
Token Session Interceptor | tokenSession | 和Token Interceptor一样,不过双击的时候把请求的数据存储在Session中 |
Validation Interceptor | validation | 使用action-validation.xml文件中定义的内容校验提交的数据。 |
Workflow Interceptor | workflow | 调用Action的validate方法,一旦有错误返回,重新定位到INPUT画面 |
Parameter Filter Interceptor | N/A | 从参数列表中删除不必要的参数 |
Profiling Interceptor | profiling | 通过参数激活profile |
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_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>struts2_20120315_01</display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
struts.xml,在struts中定义了自定义的拦截器AuthInterceptor,在Action中引入了自定的拦截器,同时也引入了默认的系统拦截器栈defaultStack;
这里需要注意一点就是拦截器的引入顺序~~~~先引入defaultStack,再引入自定义的AuthInterceptor。
<?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>
<constant name="struts.devMode" value="true" />
<package name="default" extends="struts-default">
<interceptors>
<interceptor name="AuthInterceptor"
class="com.zeph.struts2.interceptor.AuthInterceptor"></interceptor>
</interceptors>
<action name="LoginAction" class="com.zeph.struts2.action.LoginAction">
<result name="success">/success.jsp</result>
<result name="error">/error.jsp</result>
<result name="input">/index.jsp</result>
<interceptor-ref name="defaultStack" />
<interceptor-ref name="AuthInterceptor" />
</action>
</package>
</struts>
LoginAction.java 在execute方法中,删除了验证userName==admin,将此功能写入到自定义的拦截器中AuthInterceptor。
package com.zeph.struts2.action;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
private static final long serialVersionUID = 1L;
private String userName;
private String password;
private ActionContext context;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public void validate() {
if (getUserName().length() == 0) {
addFieldError("userName", "User Name is required.");
}
}
@Override
public String execute() throws Exception {
context = ActionContext.getContext();
Integer count = (Integer) context.getApplication().get("count");
if (count == null) {
count = 1;
} else {
count++;
}
// 删除了admin的用户名的判断,把它放在自定义的拦截器中
context.getApplication().put("count", count);
context.getSession().put("userName", getUserName());
return SUCCESS;
}
}
AuthInterceptor.java 自定的拦截器,方法是通过实现Interceptor接口来实现。
package com.zeph.struts2.interceptor;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.zeph.struts2.action.LoginAction;
public class AuthInterceptor implements Interceptor {
private static final long serialVersionUID = 1L;
@Override
public void destroy() {
}
@Override
public void init() {
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
LoginAction loginAction = (LoginAction) invocation.getAction();
String userName = loginAction.getUserName();
if (userName != null && userName.equals("admin")) {
return invocation.invoke();
} else {
return Action.INPUT;
}
}
}
index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>login</title>
</head>
<body>
<s:form action="LoginAction">
<s:textfield name="userName" label="UserName"/>
<s:password name="password" label="Password"/>
<s:submit/>
</s:form>
</body>
</html>
success.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login Success</title>
</head>
<body>
<s:property value="#session.userName" />
,login success,
<a href="context.jsp">redirect to context</a>. times:
<s:property value="#application.count" />
</body>
</html>
error.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>error</title>
</head>
<body>
<P>Sorry, login failed!!!!</P>
</body>
</html>
context.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib uri="/struts-tags" prefix="s"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>context page</title>
</head>
<body>
welcome,
<s:property value="#session.userName" />
.
<script language="JavaScript" type="text/javascript">
var today;
var day;
var date;
today = new Date();
if (today.getDay() == 0)
day = "Sunday ";
if (today.getDay() == 1)
day = "Monday";
if (today.getDay() == 2)
day = "Tuesday ";
if (today.getDay() == 3)
day = "Wednesday";
if (today.getDay() == 4)
day = "Thursday ";
if (today.getDay() == 5)
day = "Friday";
if (today.getDay() == 6)
day = "Saturday ";
date = "Today is " + (today.getYear()) + "/" + (today.getMonth() + 1)
+ "/" + today.getDate();
document.write(date);
document.write(" " + day);
</script>
</body>
</html>