主要介绍Struts2的拦截器,结合实例说明。
代码结构:
关键代码:
LoginCheckInterceptor.javapackage com.alfred.interceptor;
import com.alfred.login.action.LoginAction;
import com.alfred.regist.action.RegistAction;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
public class LoginCheckInterceptor extends MethodFilterInterceptor {
@Override
protected String doIntercept(ActionInvocation invocation) throws Exception {
System.out.println("LoginCheckInterceptor doIntercept begin");
String ret = null;
if (LoginAction.class == invocation.getAction().getClass()
|| RegistAction.class == invocation.getAction().getClass()) {
ret = invocation.invoke();
} else {
Object obj = invocation.getInvocationContext().getSession().get(
"user");
if (obj != null) {
ret = invocation.invoke();
} else {
return Action.LOGIN;
}
}
System.out.println("LoginCheckInterceptor doIntercept end");
return ret;
}
}
MapParametersInterceptor.javapackage com.alfred.interceptor;
import java.util.Map;
import java.util.TreeMap;
import com.opensymphony.xwork2.interceptor.ParametersInterceptor;
import com.opensymphony.xwork2.util.ValueStack;
public class MapParametersInterceptor extends ParametersInterceptor {
protected void setParameters(final Object action, ValueStack stack,
final Map<String, Object> parameters) {
System.out.println("MapParametersInterceptor setParameters begin");
Map<String, Object> params;
params = new TreeMap<String, Object>(parameters);
for (Map.Entry<String, Object> entry : params.entrySet()) {
String name = entry.getKey().toString();
if (!name.contains(".")) {
name = "map." + name;
}
Object value = entry.getValue();
if (isAcceptableParameter(name, action) && isAcceptableValue(value)) {
if (value != null) {
Object[] tmp = (Object[]) value;
if (tmp.length == 1) {
value = tmp[0];
if (value != null
&& value.toString().trim().length() > 0) {
if (value instanceof String) {
value = value.toString().trim();
}
stack.setValue(name, value);
// stack.setParameter(name, value);
}
} else {
StringBuffer sb = new StringBuffer();
for (int x = 0; x < tmp.length; x++) {
value = tmp[x];
if (value != null
&& value.toString().trim().length() > 0) {
sb.append(value).append(",");
}
}
if (sb.length() > 0) {
sb = sb.delete(sb.length() - 1, sb.length());
}
stack.setValue(name, sb.toString());
}
}
}
}
System.out.println("MapParametersInterceptor setParameters end");
}
}
TimeInterceptor.javapackage com.alfred.interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class TimeInterceptor implements Interceptor {
public void destroy() {
System.out.println("timeInterceptor destroy");
}
public void init() {
System.out.println("timeInterceptor init");
}
public String intercept(ActionInvocation invocation) throws Exception {
long begin = System.currentTimeMillis();
System.out.println("TimeInterceptor intercept begin:" + begin);
System.out.println("TimeInterceptor:"
+ invocation.getAction().getClass() + ":"
+ invocation.getProxy().getMethod());
// 调用下一步
String ret = invocation.invoke();
long end = System.currentTimeMillis();
long interval = end - begin;
System.out.println("TimeInterceptor intercept end:" + end
+ " interval:" + interval);
return ret;
}
}
LoginAction.javapackage com.alfred.login.action;
import java.util.HashMap;
import java.util.Map;
import com.alfred.user.bean.User;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction extends ActionSupport {
private Map<Object, Object> map = new HashMap<Object, Object>();
public String loginAction() {
System.out.println("LoginAction loginAction");
map.put("type", "login");
String username = map.containsKey("username") ? map.get("username")
.toString() : "null";
String password = map.containsKey("password") ? map.get("password")
.toString() : "null";
System.out.println("username:" + username);
System.out.println("password:" + password);
if (username.equals("admin")) {
User user = new User();
user.setUsername(username);
user.setPassword(password);
// 存入session
ActionContext.getContext().getSession().put("user", user);
return SUCCESS;
} else {
return LOGIN;
}
}
public Map<Object, Object> getMap() {
return map;
}
public void setMap(Map<Object, Object> map) {
this.map = map;
}
}
RegistAction.javapackage com.alfred.regist.action;
import java.util.HashMap;
import java.util.Map;
import com.alfred.user.bean.User;
import com.opensymphony.xwork2.ActionSupport;
public class RegistAction extends ActionSupport {
private Map<Object, Object> map = new HashMap<Object, Object>();
private User user;
public String registAction() {
System.out.println("RegistAction registAction");
map.put("type", "regist");
return SUCCESS;
}
public Map<Object, Object> getMap() {
return map;
}
public void setMap(Map<Object, Object> map) {
this.map = map;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
UserAction.javapackage com.alfred.user.action;
import java.util.HashMap;
import java.util.Map;
import com.alfred.user.bean.User;
import com.opensymphony.xwork2.ActionSupport;
public class UserAction extends ActionSupport {
private Map<Object, Object> map = new HashMap<Object, Object>();
private User user = new User();
@Override
public String execute() throws Exception {
System.out.println("UserAction execute");
map.put("username", user.getUsername());
map.put("password", user.getPassword());
return SUCCESS;
}
public Map<Object, Object> getMap() {
return map;
}
public void setMap(Map<Object, Object> map) {
this.map = map;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
User.javapackage com.alfred.user.bean;
public class User {
private String username;
private String password;
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;
}
}
main.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>
</head>
<body>
type:<s:property value="map.type" /><br />
<s:if test="map.type == 'login'">
username:<s:property value="map.username" /><br />
password:<s:property value="map.password" />
</s:if>
<s:elseif test="map.type == 'regist'">
username:<s:property value="user.username" /><br />
password:<s:property value="user.password" />
</s:elseif>
<s:else>
you are error
</s:else>
</body>
</html>
login.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>
</head>
<body>
login<br />
<s:form action="login">
<s:textfield name="username" label="username"></s:textfield>
<s:password name="password" label="password"></s:password>
<s:token></s:token>
<s:submit value="submit"></s:submit>
</s:form>
<s:debug></s:debug>
</body>
</html>
regist.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>
</head>
<body>
regist<br />
<s:form action="regist">
<s:textfield name="user.username" label="username"></s:textfield>
<s:password name="user.password" label="password"></s:password>
<s:submit value="submit"></s:submit>
</s:form>
</body>
</html>
struts.xml<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="system" extends="struts-default">
<interceptors>
<!-- 拦截器定义 -->
<interceptor name="TimeInterceptor" class="com.alfred.interceptor.TimeInterceptor" />
<interceptor name="MapParametersInterceptor" class="com.alfred.interceptor.MapParametersInterceptor" />
<interceptor name="LoginCheckInterceptor" class="com.alfred.interceptor.LoginCheckInterceptor" />
<!-- 拦截器栈定义 -->
<interceptor-stack name="myDefaultStack">
<interceptor-ref name="TimeInterceptor"></interceptor-ref>
<interceptor-ref name="MapParametersInterceptor"></interceptor-ref>
<!-- 判断是否已登录 -->
<interceptor-ref name="LoginCheckInterceptor">
<!-- 允许执行的方法 如下只有execute这个方法允许调用该拦截器
<param name="includeMethods">execute</param> -->
<!-- 不允许执行的方法 如果有和includeMethods相同的,以includeMethods为主
<param name="excludeMethods">loginAction,registAction</param> -->
</interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 默认拦截器,在action没有加拦截器的情况下,默认加入 -->
<default-interceptor-ref name="myDefaultStack"></default-interceptor-ref>
<!-- 全局result配置,会加入各个action -->
<global-results>
<result name="login">login.jsp</result>
<result name="input">WEB-INF/jsp/error.jsp</result>
</global-results>
<action name="login"
class="com.alfred.login.action.LoginAction" method="loginAction">
<result name="success" type="redirectAction">
<param name="actionName">user</param>
<param name="type">${map.type}</param>
<param name="user.username">${map.username}</param>
<param name="user.password">${map.password}</param>
</result>
<!-- session加入token,避免重复提交
<interceptor-ref name="token"></interceptor-ref>
-->
</action>
<action name="regist" class="com.alfred.regist.action.RegistAction" method="registAction">
<result name="success">WEB-INF/jsp/main.jsp</result>
</action>
<action name="user" class="com.alfred.user.action.UserAction">
<result name="success">WEB-INF/jsp/main.jsp</result>
</action>
</package>
</struts>
实例程序中定义了三个拦截器:TimeInterceptor、MapParametersInterceptor、LoginCheckInterceptor,分别是时间捕捉、Map传值、登陆验证功能。
TimeInterceptor实现了Interceptor接口,该接口是Struts2中拦截器的祖先,所有拦截器的最终实现。该接口有三个方法,分别是init、destory、intercept,分别是初始化时执行、销毁时执行、以及具体的拦截操作。同时,Struts2中还定义了一个抽象类AbstractInterceptor,该类实现了Interceptor接口,并空实现了init和destory方法,所以我们也可以直接通过继承AbstractInterceptor类来自定义拦截器(好处是不用实现init和destory方法,和实现Interceptor接口的区别也就在此,实际上这两个方法一般不会去使用)
LoginCheckInterceptor继承自抽象类MethodFilterInterceptor,MethodFilterInterceptor类在AbstractInterceptor的基础上添加了方法过滤,所谓的方法过滤就是允许我们指定哪些action方法要进行拦截处理,哪些action方法不进行action拦截处理,分别定义在excludeMethods(不拦截)和includeMethods(要拦截)中。以下是相关的源码,可以看到MethodFilterInterceptor在intercept实现里添加了一层判断applyInterceptor,用于判断是否执行拦截。也因此继承自MethodFilterInterceptor的子类一般不去实现intercept方法,而是实现MethodFilterInterceptor提供的doIntercept方法(应为如果不实现doIntercept而直接实现intercept,那就等于没继承MethodFilterInterceptor)
MethodFilterInterceptor.java/**
* <!-- START SNIPPET: javadoc -->
*
* MethodFilterInterceptor is an abstract <code>Interceptor</code> used as
* a base class for interceptors that will filter execution based on method
* names according to specified included/excluded method lists.
*
* <p/>
*
* Settable parameters are as follows:
*
* <ul>
* <li>excludeMethods - method names to be excluded from interceptor processing</li>
* <li>includeMethods - method names to be included in interceptor processing</li>
* </ul>
*
* <p/>
*
* <b>NOTE:</b> If method name are available in both includeMethods and
* excludeMethods, it will be considered as an included method:
* includeMethods takes precedence over excludeMethods.
*
* <p/>
*
* Interceptors that extends this capability include:
*
* <ul>
* <li>TokenInterceptor</li>
* <li>TokenSessionStoreInterceptor</li>
* <li>DefaultWorkflowInterceptor</li>
* <li>ValidationInterceptor</li>
* </ul>
*
* <!-- END SNIPPET: javadoc -->
*
* @author <a href='mailto:the_mindstorm[at]evolva[dot]ro'>Alexandru Popescu</a>
* @author Rainer Hermanns
*
* @see org.apache.struts2.interceptor.TokenInterceptor
* @see org.apache.struts2.interceptor.TokenSessionStoreInterceptor
* @see com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor
* @see com.opensymphony.xwork2.validator.ValidationInterceptor
*
* @version $Date$ $Id$
*/
public abstract class MethodFilterInterceptor extends AbstractInterceptor {
protected transient Logger log = LoggerFactory.getLogger(getClass());
protected Set<String> excludeMethods = Collections.emptySet();
protected Set<String> includeMethods = Collections.emptySet();
public void setExcludeMethods(String excludeMethods) {
this.excludeMethods = TextParseUtil.commaDelimitedStringToSet(excludeMethods);
}
public Set<String> getExcludeMethodsSet() {
return excludeMethods;
}
public void setIncludeMethods(String includeMethods) {
this.includeMethods = TextParseUtil.commaDelimitedStringToSet(includeMethods);
}
public Set<String> getIncludeMethodsSet() {
return includeMethods;
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
if (applyInterceptor(invocation)) {
return doIntercept(invocation);
}
return invocation.invoke();
}
protected boolean applyInterceptor(ActionInvocation invocation) {
String method = invocation.getProxy().getMethod();
// ValidationInterceptor
boolean applyMethod = MethodFilterInterceptorUtil.applyMethod(excludeMethods, includeMethods, method);
if (log.isDebugEnabled()) {
if (!applyMethod) {
log.debug("Skipping Interceptor... Method [" + method + "] found in exclude list.");
}
}
return applyMethod;
}
/**
* Subclasses must override to implement the interceptor logic.
*
* @param invocation the action invocation
* @return the result of invocation
* @throws Exception
*/
protected abstract String doIntercept(ActionInvocation invocation) throws Exception;
}
MethodFilterInterceptorUtil.java/**
* Utility class contains common methods used by
* {@link com.opensymphony.xwork2.interceptor.MethodFilterInterceptor}.
*
* @author tm_jee
*/
public class MethodFilterInterceptorUtil {
/**
* Static method to decide if the specified <code>method</code> should be
* apply (not filtered) depending on the set of <code>excludeMethods</code> and
* <code>includeMethods</code>.
*
* <ul>
* <li>
* <code>includeMethods</code> takes precedence over <code>excludeMethods</code>
* </li>
* </ul>
* <b>Note:</b> Supports wildcard listings in includeMethods/excludeMethods
*
* @param excludeMethods list of methods to exclude.
* @param includeMethods list of methods to include.
* @param method the specified method to check
* @return <tt>true</tt> if the method should be applied.
*/
public static boolean applyMethod(Set<String> excludeMethods, Set<String> includeMethods, String method) {
// quick check to see if any actual pattern matching is needed
boolean needsPatternMatch = false;
for (String includeMethod : includeMethods) {
if (!"*".equals(includeMethod) && includeMethod.contains("*")) {
needsPatternMatch = true;
break;
}
}
for (String excludeMethod : excludeMethods) {
if (!"*".equals(excludeMethod) && excludeMethod.contains("*")) {
needsPatternMatch = true;
break;
}
}
// this section will try to honor the original logic, while
// still allowing for wildcards later
if (!needsPatternMatch && (includeMethods.contains("*") || includeMethods.size() == 0) ) {
if (excludeMethods != null
&& excludeMethods.contains(method)
&& !includeMethods.contains(method) ) {
return false;
}
}
// test the methods using pattern matching
WildcardHelper wildcard = new WildcardHelper();
String methodCopy ;
if (method == null ) { // no method specified
methodCopy = "";
}
else {
methodCopy = new String(method);
}
for (String pattern : includeMethods) {
if (pattern.contains("*")) {
int[] compiledPattern = wildcard.compilePattern(pattern);
HashMap<String,String> matchedPatterns = new HashMap<String, String>();
boolean matches = wildcard.match(matchedPatterns, methodCopy, compiledPattern);
if (matches) {
return true; // run it, includeMethods takes precedence
}
}
else {
if (pattern.equals(methodCopy)) {
return true; // run it, includeMethods takes precedence
}
}
}
if (excludeMethods.contains("*") ) {
return false;
}
// CHECK ME: Previous implementation used include method
for ( String pattern : excludeMethods) {
if (pattern.contains("*")) {
int[] compiledPattern = wildcard.compilePattern(pattern);
HashMap<String,String> matchedPatterns = new HashMap<String, String>();
boolean matches = wildcard.match(matchedPatterns, methodCopy, compiledPattern);
if (matches) {
// if found, and wasn't included earlier, don't run it
return false;
}
}
else {
if (pattern.equals(methodCopy)) {
// if found, and wasn't included earlier, don't run it
return false;
}
}
}
// default fall-back from before changes
return includeMethods.size() == 0 || includeMethods.contains(method) || includeMethods.contains("*");
}
/**
* Same as {@link #applyMethod(Set, Set, String)}, except that <code>excludeMethods</code>
* and <code>includeMethods</code> are supplied as comma separated string.
*
* @param excludeMethods comma seperated string of methods to exclude.
* @param includeMethods comma seperated string of methods to include.
* @param method the specified method to check
* @return <tt>true</tt> if the method should be applied.
*/
public static boolean applyMethod(String excludeMethods, String includeMethods, String method) {
Set<String> includeMethodsSet = TextParseUtil.commaDelimitedStringToSet(includeMethods == null? "" : includeMethods);
Set<String> excludeMethodsSet = TextParseUtil.commaDelimitedStringToSet(excludeMethods == null? "" : excludeMethods);
return applyMethod(excludeMethodsSet, includeMethodsSet, method);
}
}
MapParametersInterceptor继承自ParametersInterceptor类,ParametersInterceptor主要功能是实现参数过滤处理,他继承自MethodFilterInterceptor类,可以定义拦截判断参数名长度,参数格式。。。,我们通过继承ParametersInterceptor实现对参数的操作,MapParametersInterceptor重载了setParameters方法,实现的功能是将不是对象的参数(例如:user.username)存放至map中(例如:username->map.username),从而在传递非对象参数时不用在Action中定义大量的属性,而是只需要定义一个map,所有的传递参数都可以从map中获取。
拦截器最关键的方法就是intercept或doIntercept,这是实现具体拦截操作的位置,该方法一个类型为ActionInvocation的参数,通过这个参数我们可以获取到ActionContext对象、调用的Action类名、调用的Action方法等等,方便我们在拦截器中对被拦截Action的判断以及操作。
//获取拦截Action类名
Class<?> cls = invocation.getAction().getClass();
//获取拦截Action方法名
String method = invocation.getProxy().getMethod();
//获取ActionContext
ActionContext actionContext = invocation.getInvocationContext();
拦截器在struts.xml中的定义
拦截器定义在interceptors标签中,定义拦截器名称和拦截器类:
<interceptor name="TimeInterceptor" class="com.alfred.interceptor.TimeInterceptor" />
拦截器栈定义在interceptors标签中,栈内包含有一系列的拦截器引用,之后加载拦截器栈就可以一次性加载该栈内的所有拦截器:
<interceptor-stack name="myDefaultStack">
<interceptor-ref name="TimeInterceptor"></interceptor-ref>
<interceptor-ref name="MapParametersInterceptor"></interceptor-ref>
<!-- 判断是否已登录 -->
<interceptor-ref name="LoginCheckInterceptor">
<!-- 允许执行的方法 如下只有execute这个方法允许调用该拦截器
<param name="includeMethods">execute</param> -->
<!-- 不允许执行的方法 如果有和includeMethods相同的,以includeMethods为主
<param name="excludeMethods">loginAction,registAction</param> -->
</interceptor-ref>
</interceptor-stack>
默认拦截器定义在package下,所谓默认拦截器,就是action在没有加载拦截器的情况下,默认加载的拦截器,通过查看struts-default.xml配置,我们可以看到struts为我们定义的默认拦截器是defaultStack,默认拦截器栈。
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="datetime"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
<interceptor-ref name="deprecation"/>
</interceptor-stack>
<default-interceptor-ref name="defaultStack"/>
可以看到defaultStack中引用了大量的拦截器,需要提到的一点就是,如果我们action中引用了一个拦截器,那么就不会再加载默认拦截器了,所以如果想要在action中引用拦截器后仍旧可以使用默认拦截器中的拦截,需在手动加上默认拦截器的引用。
另外,无论是拦截器的引用还是拦截器中都可以加入配置参数,例如:includeMethods、excludeMethods等等,具体参数可以查看API或者看对应拦截器类的属性参数。