果冻想
认真玩技术的地方

Struts2学习之自定义拦截器

前言

在这篇《Struts2学习之拦截器机制》文章中,我详细的总结了Struts2中的拦截器,以及如何使用拦截器。有的时候,Struts2框架提供的那些内建的拦截器无法满足我们的功能需求时,这个时候我们就需要手动开发自己的拦截器了。这篇文章就来总结如何定制开发一个拦截器。

实现拦截器类

要开发我们自己的拦截器,需要实现com.opensymphony.xwork2.interceptor.Interceptor接口。该接口代码如下:

public interface Interceptor extends Serializable {
    void destroy();
    void init();
    String intercept(ActionInvocation invocation) throws Exception;
}
方法名称 方法说明
init 在该拦截器被实例化之后,拦截器执行拦截之前,系统将回调该方法。对于每个拦截器而言,其init方法只执行一次。因此该方法主要用于初始化资源
destroy 该方法与init方法对应。在拦截器实例被销毁之前,系统将回调该拦截器的destroy方法,该方法用于销毁在init方法里打开的资源
intercept 该方法是用户需要实现的拦截动作。就像Action的execute方法一样,intercept方法会返回一个字符串作为逻辑视图。如果该方法直接返回了一个字符串,系统将会跳转到该逻辑视图对应的实际视图资源,不会调用被拦截的Action。该方法的ActionInvocation参数包含了被拦截的Action的引用,可以通过调用该参数的invoke方法,将控制权转给下一个拦截器,或者转给Action的execute方法

Interceptor只是一个接口,为了更方便的使用,Struts2还提供了一个AbstractInterceptor类,该类提供了一个initdestroy方法的空实现,如果我们实现的拦截器不需要打开资源,则可以无需实现这两个方法。下面就是我们继承AbstractInterceptor类,实现的一个简单的拦截器。

public class InterceptDemo extends AbstractInterceptor
{
    // 拦截器的名字
    private String name;

    public void setName(String name)
    {
        this.name = name;
    }

    public String intercept(ActionInvocation invocation) throws Exception
    {
        // 取得被拦截的Action实例
        LoginAction action = (LoginAction)invocation.getAction();

        // 打印执行开始的时间
        System.out.println(name + "拦截器的动作----------------" + "开始执行登陆Action的时间为:" + new Date());

        // 取得开始执行Action的时间
        long start = System.currentTimeMillis();

        // 执行该拦截器的后一个拦截器
        // 如果该拦截器后没有其它拦截器,则直接执行Action的execute方法
        String result = invocation.invoke();

        // 打印执行结束的时间
        System.out.println(name + "拦截器的动作----------------" + "执行完登陆Action的时间为:" + new Date());
        long end = System.currentTimeMillis();
        System.out.println(name + "拦截器的动作----------------" + "执行完该Action的事件为" + (end - start) + "毫秒");
        return result;
    }
}

上面实现了超级简单的拦截器,其中有一个非常重要的参数ActionInvocation,我们通过这个参数可以获得被拦截的Action实例,一旦取得了Action实例,几乎获得了全部的控制权,接下来就可以“胡作非为”了。

使用拦截器

使用拦截器需要以下两个步骤:

  1. 通过<interceptor .../>元素来定义拦截器
  2. 通过<interceptor-ref .../>元素来使用拦截器

对于上面的拦截器,我们的struts.xml配置如下:

<struts>
    <package name="InterceptDemo" extends="struts-default">
        <interceptors>
            <interceptor name="InterceptDemo" class="com.jellythink.practise.InterceptDemo">
                <param name="name">演示拦截器</param>
            </interceptor>
        </interceptors>
        <action name="Login" class="com.jellythink.practise.LoginAction">          
            <result name="success">/success.jsp</result>
            <interceptor-ref name="defaultStack" />
            <interceptor-ref name="InterceptDemo" />
        </action>
    </package>
</struts>

特别需要注意的是,如果为Action指定了一个拦截器,则系统默认的拦截器栈将会失去作用,为了能够继续使用默认拦截器,所以上面配置文件中显式地应用了默认拦截器。

拦截方法的拦截器

在默认情况下,如果我们为某个Action定义了拦截器,则这个拦截器会拦截该Action内的所有方法。但是在有些情况下,我们不想拦截所有的方法,只需要拦截指定的方法,此时就需要使用Struts2拦截器的方法过滤特性了。

为了实现方法过滤的特性,Struts2提供了一个MethodFilterInterceptor类,该类是AbstractInterceptor的子类。通过继承MethodFilterInterceptor类,我们就可以让拦截器支持方法过滤特性。

下面通过代码来说明问题。

public class MyFilterInterceptor extends MethodFilterInterceptor
{
    private String name; // 拦截器名字

    public void setName(String name)
    {
        this.name = name;
    }

    public String doIntercept(ActionInvocation invocation) throws Exception
    {
        // 取得被拦截的Action实例
        LoginAction action = (LoginAction)invocation.getAction();

        // 打印执行开始的时间
        System.out.println(name + "拦截器的动作----------------" + "开始执行登陆Action的时间为:" + new Date());

        // 取得开始执行Action的时间
        long start = System.currentTimeMillis();

        // 执行该拦截器的后一个拦截器
        // 如果该拦截器后没有其它拦截器,则直接执行Action的execute方法
        String result = invocation.invoke();

        // 打印执行结束的时间
        System.out.println(name + "拦截器的动作----------------" + "执行完登陆Action的时间为:" + new Date());
        long end = System.currentTimeMillis();
        System.out.println(name + "拦截器的动作----------------" + "执行完该Action的事件为" + (end - start) + "毫秒");
        return result;
    }
}

通过代码可以看到,支持方法过滤特性的拦截器和普通的拦截器几乎是一致的,只是简单的拦截器需要重写intercept方法,而支持方法过滤特性的拦截器需要实现doIntercept方法,仅此而已。但是,这又如何实现方法过滤呢?

在父类MethodFilterInterceptor中,还额外增加了以下两个方法:

  • public void setExcludeMethods(String excludeMethods):排除需要过滤的方法——设置方法“黑名单”,所有在excludeMethods字符串中列出的方法都不会被拦截;
  • public void setIncludeMethods(String includeMethods):设置需要过滤的方法——设置方法“白名单”,所有在includeMethods字符串中列出的方法都会被拦截。

下面通过一个小的DEMO来说说如何设置“黑名单”和“白名单”,以及如何使用这个方法过滤的特性。

前台JSP页面:

<body>
    <form action="Login!login" method="post">
        用户名:<input type="text" name="strName" />
        密码:<input type="password" name="strPassword"/>
        <input type="submit" value="提交"/>
    </form>

    <form action="Login!execute" method="post">
            用户名:<input type="text" name="strName" />
        密码:<input type="password" name="strPassword"/>
        <input type="submit" value="提交"/>
    </form>
</body>

Struts.xml配置:

<struts>
    <constant name="struts.enable.DynamicMethodInvocation" value="true" />
    <package name="InterceptDemo" extends="struts-default">
        <interceptors>
            <interceptor name="InterceptDemo" class="com.jellythink.practise.MyFilterInterceptor">
                <param name="name">方法过滤拦截器</param>
            </interceptor>
        </interceptors>
        <action name="Login" class="com.jellythink.practise.LoginAction">          
            <result name="success">/success.jsp</result>
            <interceptor-ref name="defaultStack" />
            <interceptor-ref name="InterceptDemo">
                <param name="excludeMethods">execute</param>
            </interceptor-ref>
        </action>
    </package>
</struts>

对于execute方法,由于加入了黑名单,所以不会被拦截,执行时就不会被拦截;而对于login方法,则会被拦截,console会打印这样的内容:

方法过滤拦截器拦截器的动作----------------开始执行登陆Action的时间为:Fri May 20 20:29:13 CST 2016
执行login方法
方法过滤拦截器拦截器的动作----------------执行完登陆Action的时间为:Fri May 20 20:29:13 CST 2016
方法过滤拦截器拦截器的动作----------------执行完该Action的事件为16毫秒

但是在使用方法过滤特性的拦截器时,需要注意以下两点:

  • 如果需要同时指定多个方法不被该拦截器拦截,则多个方法之间以英文逗号(,)隔开,例如:
    <interceptor-ref name="InterceptDemo">
        <param name="excludeMethods">execute,login</param>
    </interceptor-ref>
    
  • 如果excludeMethodsincludeMethods参数同时指定了一个方法名,则拦截器会拦截该方法。

总结

这篇文章对Struts2框架中的重点内容——拦截器,进行了比较全面的总结,虽然在实际开发中,我们很少有机会去自定义一个拦截器,但是了解和掌握拦截器是深入学习Struts2框架的基础,希望我的这篇文章对你有帮助。

果冻想,认真玩技术的地方。

2016年5月20日 于呼和浩特。

赞(40) 打赏
未经允许不得转载:果冻想 » Struts2学习之自定义拦截器
关注微信公众号
关注微信公众号和果冻一起分享你的疑惑与心得。
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

玩技术,我们是认真的

联系我们关于果冻

请我喝杯咖啡也是不错的

支付宝扫一扫打赏

微信扫一扫打赏