天天看点

Struts 2的基石——拦截器(Interceptor)

首先,要跟大家道个歉,前一阵子为给客户个一个demo,忙得不可开交,所以很久没有更新blog。提到这个demo我想顺便跟大家分享一下心得——如果大家希望快速开发,一个类似struts 2这样的简单方便的web框架必不可少。我们在开发demo使用的还是struts

1.2.8,而且没有不使用任何el(表达式语言),导致页面出现无数类似“<%= ((integer) request.getattribute("xx")).intvalue()%6 %>”的代码。struts 1.x的form bean的麻烦使得有部分同事直接使用request.getparameter(string arg),继而引入另一种麻烦。诸如此类的问题,在demo这样时间紧迫的项目凸显了struts 1.x对快速开发的无能为力。不过没办法,由于我们项目中的几个资深员工除了struts 1.x外,对其它的web框架似乎不大感兴趣。

拦截器,在aop(aspect-oriented programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作。拦截是aop的一种实现策略。

在webwork的中文文档的解释为——拦截器是动态拦截action调用的对象。它提供了一种机制可以使开发者可以定义在一个action执行的前后执行的代码,也可以在一个action执行前阻止其执行。同时也是提供了一种可以提取action中可重用的部分的方式。

谈到拦截器,还有一个词大家应该知道——拦截器链(interceptor chain,在struts 2中称为拦截器栈interceptor stack)。拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

struts 2的拦截器实现相对简单。当请求到达struts 2的servletdispatcher时,struts 2会查找配置文件,并根据其配置实例化相对的拦截器对象,然后串成一个列表(list),最后一个一个地调用列表中的拦截器,如图1所示。

Struts 2的基石——拦截器(Interceptor)

图1 拦截器调用序列图

struts 2已经为您提供丰富多样的,功能齐全的拦截器实现。大家可以到struts2-all-2.0.1.jar或struts2-core-2.0.1.jar包的struts-default.xml查看关于默认的拦截器与拦截器链的配置。

Struts 2的基石——拦截器(Interceptor)

在本文使用是struts 2的最新发布版本2.0.1。需要下载的朋友请点击以下链接:

<a target="_blank" href="http://apache.justdn.org/struts/binaries/struts-2.0.1-all.zip">http://apache.justdn.org/struts/binaries/struts-2.0.1-all.zip</a>

以下部分就是从struts-default.xml文件摘取的内容:

&lt; interceptor name ="alias" class ="com.opensymphony.xwork2.interceptor.aliasinterceptor" /&gt; 

&lt; interceptor name ="autowiring" class ="com.opensymphony.xwork2.spring.interceptor.actionautowiringinterceptor" /&gt; 

&lt; interceptor name ="chain" class ="com.opensymphony.xwork2.interceptor.chaininginterceptor" /&gt; 

&lt; interceptor name ="conversionerror" class ="org.apache.struts2.interceptor.strutsconversionerrorinterceptor" /&gt; 

&lt; interceptor name ="createsession" class ="org.apache.struts2.interceptor.createsessioninterceptor" /&gt; 

&lt; interceptor name ="debugging" class ="org.apache.struts2.interceptor.debugging.debugginginterceptor" /&gt; 

&lt; interceptor name ="external-ref" class ="com.opensymphony.xwork2.interceptor.externalreferencesinterceptor" /&gt; 

&lt; interceptor name ="execandwait" class ="org.apache.struts2.interceptor.executeandwaitinterceptor" /&gt; 

&lt; interceptor name ="exception" class ="com.opensymphony.xwork2.interceptor.exceptionmappinginterceptor" /&gt; 

&lt; interceptor name ="fileupload" class ="org.apache.struts2.interceptor.fileuploadinterceptor" /&gt; 

&lt; interceptor name ="i18n" class ="com.opensymphony.xwork2.interceptor.i18ninterceptor" /&gt; 

&lt; interceptor name ="logger" class ="com.opensymphony.xwork2.interceptor.logginginterceptor" /&gt; 

&lt; interceptor name ="model-driven" class ="com.opensymphony.xwork2.interceptor.modeldriveninterceptor" /&gt; 

&lt; interceptor name ="scoped-model-driven" class ="com.opensymphony.xwork2.interceptor.scopedmodeldriveninterceptor" /&gt; 

&lt; interceptor name ="params" class ="com.opensymphony.xwork2.interceptor.parametersinterceptor" /&gt; 

&lt; interceptor name ="prepare" class ="com.opensymphony.xwork2.interceptor.prepareinterceptor" /&gt; 

&lt; interceptor name ="static-params" class ="com.opensymphony.xwork2.interceptor.staticparametersinterceptor" /&gt; 

&lt; interceptor name ="scope" class ="org.apache.struts2.interceptor.scopeinterceptor" /&gt; 

&lt; interceptor name ="servlet-config" class ="org.apache.struts2.interceptor.servletconfiginterceptor" /&gt; 

&lt; interceptor name ="sessionautowiring" class ="org.apache.struts2.spring.interceptor.sessioncontextautowiringinterceptor" /&gt; 

&lt; interceptor name ="timer" class ="com.opensymphony.xwork2.interceptor.timerinterceptor" /&gt; 

&lt; interceptor name ="token" class ="org.apache.struts2.interceptor.tokeninterceptor" /&gt; 

&lt; interceptor name ="token-session" class ="org.apache.struts2.interceptor.tokensessionstoreinterceptor" /&gt; 

&lt; interceptor name ="validation" class ="com.opensymphony.xwork2.validator.validationinterceptor" /&gt; 

&lt; interceptor name ="workflow" class ="com.opensymphony.xwork2.interceptor.defaultworkflowinterceptor" /&gt; 

&lt; interceptor name ="store" class ="org.apache.struts2.interceptor.messagestoreinterceptor" /&gt; 

&lt; interceptor name ="checkbox" class ="org.apache.struts2.interceptor.checkboxinterceptor" /&gt; 

&lt; interceptor name ="profiling" class ="org.apache.struts2.interceptor.profilingactivationinterceptor" /&gt;

在struts-default.xml中已经配置了以上的拦截器。如果您想要使用上述拦截器,只需要在应用程序struts.xml文件中通过“&lt;include file="struts-default.xml" /&gt;”将struts-default.xml文件包含进来,并继承其中的struts-default包(package),最后在定义action时,使用“&lt;interceptor-ref name="xx" /&gt;”引用拦截器或拦截器栈(interceptor

stack)。一旦您继承了struts-default包(package),所有action都会调用拦截器栈 ——defaultstack。当然,在action配置中加入“&lt;interceptor-ref name="xx" /&gt;”可以覆盖defaultstack。

下面是关于拦截器timer使用的例子。首先,新建action类tuotrial/timerinterceptoraction.java,内容如下:

Struts 2的基石——拦截器(Interceptor)

 package tutorial;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 import com.opensymphony.xwork2.actionsupport;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 public class timerinterceptoraction extends actionsupport {

Struts 2的基石——拦截器(Interceptor)

    @override

Struts 2的基石——拦截器(Interceptor)

     public string

execute() {

Struts 2的基石——拦截器(Interceptor)

         try {

Struts 2的基石——拦截器(Interceptor)

             // 模拟耗时的操作 

Struts 2的基石——拦截器(Interceptor)

       thread.sleep( 500 );

Struts 2的基石——拦截器(Interceptor)

        } catch (exception

e) {

Struts 2的基石——拦截器(Interceptor)

            e.printstacktrace();

Struts 2的基石——拦截器(Interceptor)

        } 

Struts 2的基石——拦截器(Interceptor)

         return success;

Struts 2的基石——拦截器(Interceptor)

    } 

Struts 2的基石——拦截器(Interceptor)

}

配置action,名为timer,配置文件如下:

&lt;! doctype struts public

        "-//apache software foundation//dtd struts configuration 2.0//en"

&lt; struts &gt; 

    &lt; include file ="struts-default.xml" /&gt;  

    &lt; package name ="interceptordemo" extends ="struts-default" &gt; 

        &lt; action name ="timer" class ="tutorial.timerinterceptoraction" &gt; 

            &lt; interceptor-ref name ="timer" /&gt; 

            &lt; result &gt; /timer.jsp &lt;/ result &gt; 

        &lt;/ action &gt; 

    &lt;/ package &gt; 

&lt;/ struts &gt;

2006 - 12 - 6 14 : 27 : 32 com.opensymphony.xwork2.interceptor.timerinterceptor

dolog

信息: executed action [ //timer!execute ] took 2859 ms.

在您的环境中执行timer!execute的耗时,可能上述的时间有些不同,这取决于您pc的性能。但是无论如何,2859 ms与500 ms还是相差太远了。这是什么原因呢?其实原因是第一次加载timer时,需要进行一定的初始工作。当你重新请求timer.action时,以上输出会变为:

2006 - 12 - 6 14 : 29 : 18 com.opensymphony.xwork2.interceptor.timerinterceptor

信息: executed action [ //timer!execute ] took 500 ms.

ok,这正是我们期待的结果。上述例子演示了拦截器timer的用途——用于显示执行某个action方法的耗时,在我们做一个粗略的性能调试时,这相当有用。

作为“框架(framework)”,可扩展性是不可或缺的,因为世上没有放之四海而皆准的东西。虽然,struts 2为我们提供如此丰富的拦截器实现,但是这并不意味我们失去创建自定义拦截器的能力,恰恰相反,在struts 2自定义拦截器是相当容易的一件事。

Struts 2的基石——拦截器(Interceptor)

大家在开始着手创建自定义拦截器前,切记以下原则:

拦截器必须是无状态的,不要使用在api提供的actioninvocation之外的任何东西。

要求拦截器是无状态的原因是struts 2不能保证为每一个请求或者action创建一个实例,所以如果拦截器带有状态,会引发并发问题。

所有的struts 2的拦截器都直接或间接实现接口com.opensymphony.xwork2.interceptor.interceptor。除此之外,大家可能更喜欢继承类com.opensymphony.xwork2.interceptor.abstractinterceptor。

以下例子演示通过继承abstractinterceptor,实现授权拦截器。

首先,创建授权拦截器类tutorial.authorizationinterceptor,代码如下:

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 import java.util.map;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 import com.opensymphony.xwork2.action;

Struts 2的基石——拦截器(Interceptor)

 import com.opensymphony.xwork2.actioninvocation;

Struts 2的基石——拦截器(Interceptor)

 import com.opensymphony.xwork2.interceptor.abstractinterceptor;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 public class authorizationinterceptor extends abstractinterceptor {

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

intercept(actioninvocation ai) throws exception {

Struts 2的基石——拦截器(Interceptor)

        map session = ai.getinvocationcontext().getsession();

Struts 2的基石——拦截器(Interceptor)

        string role = (string)

session.get( " role " );

Struts 2的基石——拦截器(Interceptor)

         if ( null != role) {

Struts 2的基石——拦截器(Interceptor)

            object o = ai.getaction();

Struts 2的基石——拦截器(Interceptor)

             if (o instanceof roleaware) {

Struts 2的基石——拦截器(Interceptor)

                roleaware action = (roleaware)

o;

Struts 2的基石——拦截器(Interceptor)

                action.setrole(role);

Struts 2的基石——拦截器(Interceptor)

            } 

Struts 2的基石——拦截器(Interceptor)

             return ai.invoke();

Struts 2的基石——拦截器(Interceptor)

        } else {

Struts 2的基石——拦截器(Interceptor)

             return action.login;

Struts 2的基石——拦截器(Interceptor)

        }  

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

以上代码相当简单,我们通过检查session是否存在键为“role”的字符串,判断用户是否登陆。如果用户已经登陆,将角色放到action中,调用action;否则,拦截直接返回action.login字段。为了方便将角色放入action,我定义了接口tutorial.roleaware,代码如下:

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 public interface roleaware {

Struts 2的基石——拦截器(Interceptor)

     void setrole(string

role);

Struts 2的基石——拦截器(Interceptor)

接着,创建action类tutorial.authorizatedaccess模拟访问受限资源,它作用就是通过实现roleaware获取角色,并将其显示到showuser.jsp中,代码如下:

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 public class authorizatedaccess extends actionsupport implements roleaware {

Struts 2的基石——拦截器(Interceptor)

     private string

role;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

     public void setrole(string

role) {

Struts 2的基石——拦截器(Interceptor)

         this .role = role;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

getrole() {

Struts 2的基石——拦截器(Interceptor)

         return role;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

以下是showuser.jsp的代码:

&lt;% @ page  contenttype = " text/html;

charset=utf-8 " %&gt; 

&lt;% @taglib prefix = " s " uri = " /struts-tags " %&gt; 

&lt; html &gt; 

&lt; head &gt; 

    &lt; title &gt; authorizated

user &lt;/ title &gt; 

&lt;/ head &gt; 

&lt; body &gt; 

    &lt; h1 &gt; your

role is: &lt; s:property value ="role" /&gt;&lt;/ h1 &gt; 

&lt;/ body &gt; 

&lt;/ html &gt;

然后,创建tutorial.roles初始化角色列表,代码如下:

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 import java.util.hashtable;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 public class roles {

Struts 2的基石——拦截器(Interceptor)

     public map &lt; string,

string &gt; getroles() {

Struts 2的基石——拦截器(Interceptor)

        map &lt; string,

string &gt; roles = new hashtable &lt; string,

string &gt; ( 2 );

Struts 2的基石——拦截器(Interceptor)

        roles.put( " employee " , " employee " );

Struts 2的基石——拦截器(Interceptor)

        roles.put( " manager " , " manager " );

Struts 2的基石——拦截器(Interceptor)

         return roles;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

接下来,新建login.jsp实例化tutorial.roles,并将其roles属性赋予&lt;s:radio&gt;标志,代码如下:

    &lt; title &gt; login &lt;/ title &gt; 

    &lt; h1 &gt; login &lt;/ h1 &gt; 

    please select a role below:

    &lt; s:bean id ="roles" name ="tutorial.roles" /&gt; 

    &lt; s:form action ="login" &gt; 

        &lt; s:radio list ="#roles.roles" value ="'employee'" name ="role" label ="role" /&gt; 

        &lt; s:submit /&gt; 

    &lt;/ s:form &gt; 

创建action类tutorial.login将role放到session中,并转到action类tutorial.authorizatedaccess,代码如下:

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 import org.apache.struts2.interceptor.sessionaware;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

 public class login extends actionsupport implements sessionaware {

Struts 2的基石——拦截器(Interceptor)

role;    

Struts 2的基石——拦截器(Interceptor)

     private map

session;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

     public void setsession(map

session) {

Struts 2的基石——拦截器(Interceptor)

         this .session = session;

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

        session.put( " role " ,

Struts 2的基石——拦截器(Interceptor)
Struts 2的基石——拦截器(Interceptor)

    }  

Struts 2的基石——拦截器(Interceptor)

最后,配置struts.xml文件,内容如下:

        &lt; interceptors &gt; 

            &lt; interceptor name ="auth" class ="tutorial.authorizationinterceptor" /&gt; 

        &lt;/ interceptors &gt; 

        &lt; action name ="login" class ="tutorial.login" &gt; 

            &lt; result type ="chain" &gt; authorizatedaccess &lt;/ result &gt; 

        &lt; action name ="authorizatedaccess" class ="tutorial.authorizatedaccess" &gt; 

            &lt; interceptor-ref name ="auth" /&gt; 

            &lt; result name ="login" &gt; /login.jsp &lt;/ result &gt; 

            &lt; result name ="success" &gt; /showrole.jsp &lt;/ result &gt; 

Struts 2的基石——拦截器(Interceptor)

图2 login.jsp

选中employee,点击submit,出现图3所示页面:

Struts 2的基石——拦截器(Interceptor)

图3 showrole.jsp

拦截器是struts 2比较重要的一个功能。通过正确地使用拦截器,我们可以编写高可复用的代码。