【struts2】struts2拦截器

struts2提供面向切面(AOP)编程的机制,拦截器便是一种成熟的AOP编程思想的实现,它提供一种机制使开发者能把相对独立的代码抽象出来,配置到action前后执行。拦截器interceptor类似于Filter,在执行action方法前后执行。

就像多个Filter组成了Filter链,多个拦截器也组成了拦截器链。struts2中称拦截器链为拦截器栈。拦截器栈是按顺序配置的多个拦截器,在执行action前后被依次调用

拦截器是用于在Action或者某个方法调用之前进行特定的处理的对象,类似于过滤器,是一个特殊的Servlet。拦截器主要用于在调用Action或Action方法之前做输入校验、权限验证,字符编码转换等一些通用操作。多个拦截器构成拦截器栈。Struts2中有一个默认的拦截器栈,在每个Action调用之前执行。

拦截器有三个对象:被拦截对象,拦截器对象本身,代理。

真正执行的不想目标对象,而代理。

拦截器要实现Interceptor接口,默认实现拦截器时继承AbstractInterceptor类,而很少直接继承Interceptor接口。

1、类拦截器和方法拦截器:

(1)、针对Action进行拦截处理的拦截器是类拦截器,是一种粗粒度的拦截器。

拦截器是一种特殊的Servlet,与过滤器类似,其init()和destroy()方法只执行一次。而intercepte()方法是action每次执行时都会执行一次。

(2)、只对Action中某个特定方法进行拦截处理的拦截器就是方法拦截器,方法拦截器与类拦截器不同,它继承MethodFilterInterceptor类,实现doIntercepte()方法。

2、配置类拦截器

(1)、在Struts2的配置文件struts.xml中的<package>元素标签下添加:

<interceptors>  
       <interceptor name=”拦截器名称” class=”拦截器的全路径”>  
              <!--给拦截器配置参数,如果拦截器不需要参数,下面这句就不需要了-->  
              <param name=”参数名称”>参数值</param>  
       </interceptor ></interceptors>  

注意:在配置拦截器时传递了参数,需要在拦截器中定义和参数名称相同的属性,并且生成set和get方法。定义时为默认参数,拦截器引用时参数为动态参数,动态参数覆盖默认参数。

(2)、对Action引入拦截器:

在Struts2的配置文件struts.xml中要引入拦截器的Action的<action>元素标签下添加:


<interceptor-ref name=”拦截器名称”/> 
<!-- 默认拦截器--> 
<interceptor-ref name="defaultStack" />

3、配置方法拦截器:

(1).在Struts2的配置文件struts.xml中的<package>元素标签下添加内容,与配置类拦截器相同。

(2)、Action中引入方法拦截器:

在Struts2的配置文件struts.xml中要引入拦截器的Action的<action>元素标签下添加:

<interceptor-ref name=”方法拦截器名称”>  
       <!--对该Action要引入的方法拦截器的方法-->  
       <param name=”includeMethods”>Action方法1,Action方法2,…</param>  
<!-- 对该Action要排除的方法拦截器的方法-->  
       <param name=”excludeMethods”>Action方法3,Action方法4,…</param>  
</interceptor-ref>  

注意:include的优先级高于exclude。

4、配置拦截器栈

多个拦截器构成拦截器栈。

(1).拦截器栈定义方法:

在Struts2的配置文件struts.xml中的<package>元素标签下添加:

<interceptor-stack name=”拦截器栈名称”>  
       <interceptor-ref name=”拦截器名称”>  
<interceptor-stack> 

注意:拦截器栈中可以包含拦截器和拦截器栈。

(2)、在Action中引入拦截器栈:

和引入拦截器相同

<interceptor-ref name=”拦截器栈名称”/>  
<!-- 默认拦截器-->
<interceptor-ref name="defaultStack" />

注意:,Struts2的Action配置了一个默认的拦截器栈,如果在Action中引入了自定义的拦截器或者拦截器栈之后,默认的Struts2默认的拦截器和拦截器栈就不会自动添加到该Action上,如果想使用默认的拦截器和拦截器栈就必须手动添加。

==========================================

实例一、timer计时拦截器

==========================================

timer拦截器能够统计每个action方法运行所需要的时间。它的原理是在action执行前开启记录一下时间,action执行后再记录一下时间,然后计算两个时间差,并将时间差打印出来(在Filter中也能实现类似的功能)。下面使用timer拦截器统计一个action的运行时间。

TimerAction.java

package com.lmb.struts2.action;

import com.opensymphony.xwork2.ActionSupport;

public class TimerAction extends ActionSupport{

    @Override
    public String execute() throws Exception {
        Thread.sleep(1000);//线程沉睡一秒钟
        return SUCCESS;//返回成功页面
    }
}

该action非常简单,让当前线程沉睡1秒钟,模拟一下繁忙的业务。action配置中要配置上timer拦截器

struts.xml

<package name="main" extends="struts-default"><!-- 定义一个package-->
        <!-- 使用timer.action测试拦截器 -->
        <action name="timer" class="com.lmb.struts2.action.TimerAction" >
            <interceptor-ref name="timer"></interceptor-ref><!-- 配置Timer拦截器 -->
            <interceptor-ref name="defaultStack" />
            <result name="success">/timerSuccess.jsp</result>
        </action>
</package>

注意:package需要继承struts-default才能使用struts2里的拦截器,因为time计时器是配置在struts-default包里的。

部署后访问http://localhost:8080/struts2/timer.action,控制台会输出如下信息:

信息:Executed action [//timer!execute] took 1032 ms.

显示的是执行timer!execute所消耗的时间(1032毫秒)。第一次访问时由于需要加载拦截器,消耗的时间可能要多一些。显示action执行的时间,在粗略的估计系统性能时很有用

==========================================

实例二、token防重复提交拦截器

==========================================

如果网络不好,提交数据后可能就没有反应了。这时候用户不知道数据是否提交到服务器了,往往还会单击提交按钮再提交一次。对于某一些应用,例如:网络购物、银行转账等,重复提交会导致非常严重的后果。

token拦截器用于保证表单只被提交一次,如果再次提交表单,拦截器会拦截该请求,防止提交重复数据。它的原理是,显示表单的时候,在session中对该表单做一个标记,该标记只能使用一次,使用过后就失效了,从而保证表单最多只提交一次数据。重复提交数据会因为标记已失效而提交失败。

下面用token拦截器测试下面的TokenAction,保证在提交该表单数据时只能提交一次。TokenAction中有一个NAMES属性,用来存储提交过的所有数据。重复提交数据时,如果能提交进来,NAMES中将会积累重复的数据,以此来判断数据时候被重复提交。

TokenAction.java

package com.lmb.struts2.action;

import java.util.ArrayList;
import java.util.List;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;

public class TokenAction extends ActionSupport{

    //static属性,模拟数据库,用来存储提交过的所有数据
    public static final List<String> NAMES=new ArrayList<String>();
    private String name;

    @Override
    public String execute(){
        NAMES.add(name);//添加到NAMES中
        return Action.SUCCESS;//返回成功页面
    }

    public void setName(String name){//name属性的setter方法
        this.name=name;
    }

    public static List<String> getNAMES(){//name属性的getter方法
        return NAMES;
    }

}

action代码没有什么特别之处,就是普通的action,没有任何特殊处理。token拦截器是配置式使用的,只需要在action中配置一下即可

struts.xml

<action name="token" class="com.lmb.struts2.action.TokenAction" >
             <!-- token拦截器 -->
            <interceptor-ref name="token" />
            <!-- basicStack拦截 -->
            <interceptor-ref name="basicStack" /> 
            <interceptor-ref name="defaultStack" />
            <result>/tokenSuccess.jsp</result>
            <result name="input">/tokenInput.jsp</result>
            <result name="invalid.token">/tokenInvalid.jsp</result><!-- 如果重复提交数据,struts2将转到该页面 -->
</action>

代码中配置了token拦截器和basicStack拦截器栈。basicStack是几个常用的拦截器的集合。basicStack是必须配置的拦截器,如果不配置,struts2就不会自动向action赋值。action中还必须配置一个名为invalid.token的result页面。如果重复提交数据,struts2将转到该页面中,该结果也是必须要配置的。

tokenInput.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="/struts-tags" prefix="struts" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <body>
    <struts:form action="token">
        <struts:token /> <!-- 非常重要的token标记 -->
        <struts:label value="避免重复输入"></struts:label>
        <struts:textfield name="name" label="请输入姓名"></struts:textfield>
        <struts:submit name="提交"></struts:submit>
    </struts:form>
  </body>
</html>

JSP输入页面也需要添加一个<struts:token />标签。该标签会在session中生成所在表单的标识,该标签一定要添加在Form中,否则该拦截器无效。

输入姓名那个,然后单击“提交“按钮提交数据后,会转到成功页面,并提示已经提交的所有数据。成功页面的代码如下:

tokenSuccessjsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="/struts-tags" prefix="struts" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <body>
    已输入的姓名:<struts:property value="NAMES"/><br><br>
    <a href="tokenInput.action">&lt;&lt;重新输入</a>
  </body>
</html>

提交完成后,单击浏览器的”刷新“按钮。浏览器会弹出对话框,询问是否要重新提交数据。单击”是“按钮,浏览器会重新提交数据,然后转到tokenInvalid.jsp页面,提示输入无效。

tokenInvalid.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="/struts-tags" prefix="struts" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <body>
    输入无效。<br>
    <a href="tokenInput.action">&lt;&lt;重现输入</a>
  </body>
</html>

==========================================

实例三、execAndWait执行等待拦截器

==========================================

如果某个action运行时间比较长,浏览器会因为等待服务器响应而长时间显示空白。这比较令人烦,如果能显示一个服务器正忙的页面就好了。execAndWait拦截器就是实现这种效果的。execAndWait拦截器接受请求后,能判断上一个请求是否处理完毕。如果已经处理完毕,则显示结果页面,否则显示等待页面

WaitAction.java

package com.lmb.struts2.action;

import com.opensymphony.xwork2.ActionSupport;

public class WaitAction extends ActionSupport{

    @Override
    public String execute() throws Exception {
        Thread.sleep(10000);//沉睡10秒钟,模拟大数据量处理
        return SUCCESS;//返回成功页面
    }
}

代码让action所在的线程沉睡10秒,模拟业务处理繁忙。在10秒钟之内,将会显示等待页面。execAndWait拦截器配置在action中。配置如下:

struts.xml

<action name="wait" class="com.lmb.struts2.action.WaitAction"   >
            <interceptor-ref name="completeStack" /><!-- completeStack拦截器 -->
            <interceptor-ref name="execAndWait" /><!-- execAndWait属性 -->
            <interceptor-ref name="defaultStack" />
            <result>/waitSuccess.jsp</result>
            <result name="wait">/wait.jsp</result>
</action>

action配置了completeStack拦截器栈与execAndWait拦截器,还配置了一个名为wait的JSP页面。如果action繁忙,就显示wait页面。

wait.jsp

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%@ taglib uri="/struts-tags" prefix="struts" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <body>
        页面提交(下载)中。。。请等待。。。
        <script type="text/javascript">
            setTimeout("location = location;",1000);//1秒钟后刷新本页
        </script>
  </body>
</html>

wait页面每隔一秒钟刷新一次,直至显示成功页面。在浏览器中进行访问,前10秒钟会一直显示等待页面。

这是显示繁忙页面的简单方式,是struts2内置的解决方案。实际上较多的使用Ajax技术来实时显示繁忙页面。Ajax能做到页面无刷新,并能通过进度条实时显示进度

==========================================

实例四、自定义的权限验证拦截器

==========================================

拦截器可以自由扩展。所有的拦截器都实现自Interceptor接口。实现自定义拦截器也要实现Interceptor接口。一般直接继承AbstractInterceptor抽象类即可。

本例使用自定义拦截器判断用户是否登录。逻辑很简单,就是检查当前用户session中的account属性是否为空。如果为空,则转到登录页面,否则继续执行。

自定义拦截器AuthenticationInterceptor.java:

package com.lmb.struts2.interceptor;

import java.util.Map;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class AuthenticationInterceptor extends AbstractInterceptor{

    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        //通过invocation获取本次调用的上下文
        Map<String, Object> sessionValues=invocation.getInvocationContext().getSession();
        String account=(String) sessionValues.get("account");//从session中取值
        if (account == null) {//如果为空
            return Action.LOGIN;//则返回登陆页面
        }
        return invocation.invoke();//否则,正常运行
    }

}

以上代码会在action被调用之前运行。如果验证通过,代码invocation.invoke()会继续调用action。拦截器需要在package中定义。然后将该拦截器配置在AuthenticationAction(该action代码略)中。如果已经登录,该AuthenticationAction会显示一句话,表示验证通过。否则,会被拦截器拦截,返回登录页面(登陆页面是<global-results>中配置的全局<result/>)。

struts.xml

<package name="main" extends="struts-default"><!-- 定义一个package-->
        <global-results> <!-- 所有的全局result-->
            <result name="login">/login.jsp</result> <!-- 名为login的result-->
        </global-results>
        <interceptors>
            <interceptor name="authentication" class="com.lmb.struts2.interceptor.AuthenticationInterceptor"></interceptor>
        </interceptors>
        <action name="authentication" class="com.lmb.struts2.action.AuthenticationAction"   >
                <!-- action实现类 -->
                <!-- 自定义authentication拦截器 -->
        <interceptor-ref name="authentication" />
        <interceptor-ref name="defaultStack" />
            <result>/authenticationSuccess.jsp</result>
        </action>
</package>

注意:

1、关于拦截器的使用,以下是要注意的问题:

a、在struts.xml中配置拦截器时要先配置在全局Result之前!!

b、一定要注意!!使用自定义拦截器的action一定也要配置默认拦截器的引用,因为默认拦截器包含了参数的读取、session的管理等功能。 <interceptor-ref name="defaultStack" />

c、一定要注意,拦截器只能拦截action,无法拦截所有的请求,如JSP页面的访问!!!!,如果想拦截对JSP页面的访问可以使用过滤器来完成。

2、使用拦截器之后如何将有关拦截器验证情况的信息带回到相应的JSP页面???

拦截器中可写出如下代码:

ActionSupport action=(ActionSupport)invocation.getAction();
action.addActionMessage("您还没有登录或者登陆已经超时,请重新登陆!(要展示的有关拦截器验证情况的信息)");

在前台的JSP页面中通过${ action.actionMessages }取得该提示信息。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值