前言
拦截器是Struts2框架的核心功能,理解并使用拦截器有助于更灵活使用Struts2。拦截器与Servlet中的过滤器有些类似却又不尽相同。因为在Struts2中拦截器更像一个可插拔的组件,围绕Action和Result进行,可以在方法调用之前、之后使用。通过Struts2的工作流程(后面还会看到一个请求在Struts2中详细的执行流程)可以发现调用一个Action之前之后有许多的拦截器,这些拦截器都通过后才执行具体的action。对于每一个拦截器来说,可以直接返回,从而终止余下的拦截器。
从Struts2的工作流程说起
首先请看截取自官方的一张图:
从图中可以看到,从一个具体的请求到Action需要经过多个拦截器,action处理完毕之后,后续的拦截器会继续执行,最终到浏览器中。Struts2的工作流程如下:
- 请求发送给StrutsPrepareAndExecuteFilter
- StrutsPrepareAndExecuteFilter判断该请求是否是一个Struts2请求,如果是则进入第3步
- 如果是Struts2请求,则把请求交给ActionProxy,是Action的代理类
- ActionProxy创建一个ActionInvocation实例,并进行初始化
- 在执行具体的Action之前,ActionProxy会涉及相关拦截器的调用
- Action调用结束之后,会根据struts.xml文件中action的result配置对象得到对应的返回结果。调用execute方法之后,对返回结果进行渲染
- 执行后面的拦截器
- 把结果返回给浏览器
从整个请求处理过程来看,拦截器是处理的关键。ok,通过以上请求处理过程,我们知道了一个拦截器在Struts2中的工作方式。下面从编写一个简单的拦截开始,学习使用拦截器。
一个简单的拦截器
主要有两种方式:
- 实现Interceptor接口
- 继承AbstractInterceptor抽象类
编写自己的拦截器必须实现com.opensymphony.xwork2.interceptor.Interceptor接口,该接口有三个方法:init()、destroy()、intercept()。init方法在拦截器实例创建之后,intercept方法之前调用,主要用于初始化拦截器所需要的资源;destroy方法在拦截器实例销毁之前调用,用于销毁init初始化分配的资源;intercept方法则是在Action执行之前调用,可以通过invocation对象获取Action的状态,从而根据状态的不同进行需要的拦截操作。
下面以实现Interceptor接口为例,编写一个计算Action执行execute方法的时间的拦截器。代码如下:
package interceptor;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
public class TimeIntercptor implements Interceptor {
private static final long serialVersionUID = 1L;
@Override
public void destroy() {
}
@Override
public void init() {
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
long start = System.currentTimeMillis();
//执行action的execute方法
String result = invocation.invoke();
long end = System.currentTimeMillis();
System.out.println("执行execute方法的时间是" + (end - start));
return result;
}
}
在编写一个action并在struts.xml配置文件中进行配置,在浏览器中进行测试就可以得到执行execute方法的具体时间了。在编写拦截器类的时候需要注意:在拦截器中不应该有实例变量,因为拦截器是无状态的,无状态的解释是如果拦截器有状态,那么在多线程同时访问拦截器实例的时候,拦截器的状态是不可预知的。
至此,我们已经学会了如何编写一个简单的拦截器,下面介绍在拦截器中自带的拦截器哟哪些。
Struts2中自带的拦截器
自带的拦截器可以在struts-default.xml文件中得到,主要有:
- execAndWait(该拦截器可以让需要运行较长时间的action在后台运行,并向用户显示进度信息)
- exception(主要用于异常处理)
- fileUpload(用于文件上传)
- i18n(国际化的支持)
- logger(日志,记录action的开始于结束日志)
- modelDriven(支持模型驱动的拦截器)
- validation(定义自己的验证器)
开发安全验证功能的拦截器
在日常开发中,进行登录验证是很常见的。这里开发的拦截器主要实现的功能是:如果用户没有登录则提示没有登录的信息,并返回到登录页面。如果用户已经登录,则显示资源。这里主要介绍实际开发中拦截器的开发步骤。
步骤1:编写基本页面
登录页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>登录</title>
<s:head/>
</head>
</html>
<s:form action="login2">
<s:actionerror/>
<s:textfield label="用户名" name="user.username"></s:textfield>
<s:password label="密码" name="user.password"></s:password>
<s:submit value="登录"></s:submit>
</s:form>
登录成功页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>登录 | 成功</title>
</head>
<body>
<h3>
<s:property value="user.username"/>,欢迎访问struts2官方网站!
</h3>
</body>
</html>
资源页面:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>绝密资源</title>
</head>
<body>
<strong>这是绝密资源!</strong>
</body>
</html>
步骤二:编写Action
LoginAction2.java:
package action;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import bean.User;
import com.opensymphony.xwork2.ActionSupport;
public class LoginAction2 extends ActionSupport implements SessionAware {
private static final long serialVersionUID = 1L;
private User user;
private Map<String, Object> session;
//通过login!input来访问login.jsp
public String input() throws Exception{
return INPUT;
}
@Override
public String execute() throws Exception {
if("admin".equals(user.getUsername()) && "admin".equals(user.getPassword())){
System.out.println(user.getUsername()+"=" + user.getPassword());
session.put("user", user);
return SUCCESS;
}else{
addActionError("登录失败!");
return INPUT;
}
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
步骤四:编写拦截器
代码如下:
package interceptor;
import java.util.Map;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class AuthenticationInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 1L;
/**
* 对登录与否进行拦截验证
*/
@Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext context = ActionContext.getContext();
Map<String, Object> session = context.getSession();
Object user = session.get("user");
if(user == null){
//如果用户未登录,则返回登录页面,并添加错误信息
ActionSupport action = (ActionSupport) invocation.getAction();
action.addActionError("您还没有登录,请先登录!");
return action.LOGIN;
}else{
//如果用户已经登录,则执行后面的拦截器方法
return invocation.invoke();
}
}
}
步骤五:在struts.xml中进行配置
<interceptors>
<interceptor name="auth" class="interceptor.AuthenticationInterceptor" />
<interceptor-stack name="securityStack">
<interceptor-ref name="defaultStack" />
<interceptor-ref name="auth" />
</interceptor-stack>
</interceptors>
<global-results>
<result name="login">/WEB-INF/pages/login.jsp</result>
</global-results>
<action name="login2" class="action.LoginAction2">
<result name="input">/WEB-INF/pages/login.jsp</result>
<result>/WEB-INF/pages/success.jsp</result>
</action>
对于受保护的资源引用上面的拦截器即可
<action name="resource" class="action.ResourceAction">
<result>/WEB-INF/pages/resource.jsp</result>
<interceptor-ref name="annotatedStack" />
</action>
步骤六:在浏览器中输入http:localhost:8090/struts2/login2!input进行测试。
至此,一个安全验证的拦截器就开发完毕。
拦截器小结
从开发过程可以看待,拦截器的作用是Action的某个状态进行拦截操作,使用拦截器可以更方便处理业务逻辑。除了以上方式的开发拦截器外还有注解方式,不过注解方式的一个明显缺点是不利于代码的复用,而且注解的底层使用反射的方式完成的,所以使用注解开发,性能是一个值得考虑的问题。
以上内容转载自https://blog.csdn.net/u011116672/article/details/50381656