Struts2框架学习之六:理解并使用拦截器

  1. 云栖社区>
  2. 博客>
  3. 正文

Struts2框架学习之六:理解并使用拦截器

rhwayfun 2015-12-22 18:28:00 浏览1383
展开阅读全文

前言

拦截器是Struts2框架的核心功能,理解并使用拦截器有助于更灵活使用Struts2。拦截器与Servlet中的过滤器有些类似却又不尽相同。因为在Struts2中拦截器更像一个可插拔的组件,围绕Action和Result进行,可以在方法调用之前、之后使用。通过Struts2的工作流程(后面还会看到一个请求在Struts2中详细的执行流程)可以发现调用一个Action之前之后有许多的拦截器,这些拦截器都通过后才执行具体的action。对于每一个拦截器来说,可以直接返回,从而终止余下的拦截器。

从Struts2的工作流程说起

首先请看截取自官方的一张图:

struts2工作流程

从图中可以看到,从一个具体的请求到Action需要经过多个拦截器,action处理完毕之后,后续的拦截器会继续执行,最终到浏览器中。Struts2的工作流程如下:

  1. 请求发送给StrutsPrepareAndExecuteFilter
  2. StrutsPrepareAndExecuteFilter判断该请求是否是一个Struts2请求,如果是则进入第3步
  3. 如果是Struts2请求,则把请求交给ActionProxy,是Action的代理类
  4. ActionProxy创建一个ActionInvocation实例,并进行初始化
  5. 在执行具体的Action之前,ActionProxy会涉及相关拦截器的调用
  6. Action调用结束之后,会根据struts.xml文件中action的result配置对象得到对应的返回结果。调用execute方法之后,对返回结果进行渲染
  7. 执行后面的拦截器
  8. 把结果返回给浏览器

从整个请求处理过程来看,拦截器是处理的关键。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的某个状态进行拦截操作,使用拦截器可以更方便处理业务逻辑。除了以上方式的开发拦截器外还有注解方式,不过注解方式的一个明显缺点是不利于代码的复用,而且注解的底层使用反射的方式完成的,所以使用注解开发,性能是一个值得考虑的问题。

网友评论

登录后评论
0/500
评论
rhwayfun
+ 关注