您的位置:首页 > 编程语言 > Java开发

Struts 2的拦截器

2011-02-12 17:55 260 查看

1.拦截器的意义

避免重复代码调用

DRY规则即Don’tReapeatYourself,即不重复编写你的代码,Struts2中的拦截器符合DRY规则。从代码角度来看,拦截器本质就是一个JAVA类,这个类的某些方法较特殊,框架执行目标方法调用之前首先执行拦截器中的这些特殊方法。使用了拦截器可避免在代码中手工调用这些方法。

实现高层次的代码解耦

目标代码无需手动调用目标方法,而是由框架完成,从而将这种调用代码层次上升到更高层次,提供更高层次的解耦。

2.Struts2内建拦截器

Strus2框架内建了大量的拦截器完成了框架几乎70%的工作,比如,params拦截器将HTTP请求中的参数解析出来,设置成Action的属性;servlet-config拦截器直接将HTTP请求中的HttpServletRequest实例和HttpServletResponse实例传给Action;fileUpload拦截器负责解析请求参数中的文件域,并将一个文件域设置成Action的3个属性...

由于默认的struts拦截器栈中定义了许多框架的常用操作,且若Action中使用了自定义拦截器,struts-default包中定义的defaultStack将不起作用,从而无法完成HTTP参数映射到Action各属性的过程,因此大部分情况下,为某个Action指定了相应的拦截器后,组合使用多个拦截器/拦截器栈,或者自定义一个拦截器栈,其中包含对defaultStack的引用。

3.拦截器的实现原理

定义接口


publicinterfaceDog{ publicvoidinfo(); publicvoidrun(); }


定义接口实现类


publicclassDogImplimplementsDog{ publicvoidinfo(){ ... } publicvoidrun(){ ... } }


系统拦截器类


publicclassDogIntercepter{ publicvoidmethod1(){} publicvoidmethod2(){} }

上面只是些普通的JAVA类,实现拦截器功能关键是下面的代理类



publicclassProxyHandlerimplemtsInvocationHandler{
privateObjecttarget;//需被代理的目标对象

//创建拦截器实例
DogIntercepterdi=newDogIntercepter();
//执行代理的目标方法时,该Invoke方法会被自动调用
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args){
Objectresult=null;
if(method.getName().equals("info"){
di.methord1();
result=method.invoke(tart,args);
di.method2();
}
else{
result=methos.invoke(target,args);
}
returnresult;
}

publicvoidsetTarget(Objecto){
this.target=o;
}
}



代理工厂类


publicclassMYProxyFactory{
publicstaticObjectgetProxy(Objectobject){
ProxyHandlerhandler=newProxyHandler();
handler.setTarget(object);
returnProxy.newProxyInstance(Dogimpl.class.getClassLoader(),
object.getClass().getInterfaces(),handler);
}
}


主程序


publicclassTest{
publicstaticvoidmain(String[]args){
DogtargetObject=newDogImpl();
Dogdog=null;

//以目标对象创建代理
Objectproxy=MyProxyFactory.getProxy(targetObject);
if(proxyinstanceofDog){
dog=(Dog)proxy;
}

dog.info();
dog.run();
}
}

上例通过JDK动态代理,在执行目标方法之前调用拦截器方法一,在执行目标方法之后调用拦截器方法二。

4.拦截器及拦截器栈的配置

在Struts.xml中配置拦截器


<interceptorname="name"class="class"/>


还可以指定拦截器参数


<interceptorname="name"class="class">

<paramname="paramname">…</param>

</interceptor>



在Struts.xml中配置拦截器栈

拦截器栈是由多个拦截器组成的,即一个拦截器栈包含了多个拦截器。


<interceptor-stackname="stackname">

<interceptor-refname="interceptor1"/>

<interceptor-refname="interceptor2"/>

<interceptor-refname="interceptorstack1"/>

</interceptor>


提示:一个包中所有的拦截器都应定义在<interceptors…/>元素中

5.拦截器及拦截器栈的使用

通过<interceptor-ref…/>元素来使用拦截器和拦截器栈。下面是在Action中定义拦截器的配置示例:


<actionname="..."...>
......
<!--interceptor1-->
<interceptor-refname="int1"/>
<!--interceptor2-->
<interceptor-refname="int2">
<paramname="...">...</param>
</nterceptor-ref>
</action

注:系统为拦截器指定参数有2个时机:

定义拦截器时:即在<interceptor>和<interceptor-stack>中增加<param>子元素

使用拦截器时:即在<interceptor-ref>中增加<param>子元素

6.默认拦截器

配置每一个包时,可以为其指定默认拦截器。如果该包的某个Action未指定拦截器,系统会使用默认拦截器,但是一旦这个Action指定了拦截器则该包的默认拦截器将不起作用
配置默认拦截器使用<default-interceptor-ref…/>元素,该元素作为<packag…/>的子元素。

注:每个包只能有一个<default-interceptor-ref…/>子元素,即每个包至多只能有一个默认拦截器。


<packagename="Test"extends="struts-default">

<default-interceptor-refname="defaultStack"/>

<interceptors>

<interceptor…/>

</interceptors>

<actionname="showAction">
<result>/list.jsp</result>
</action>
</package>


提示:一个包中所有的拦截器都应定义在<interceptors…/>元素中

7.使用自定义拦截器

实现拦截器类

如果用户要开发自己的拦截器类,有如下两种方式:

实现com.opensymphony.xwork2.interceptor.Interceptor接口

继承AbstractInterceptor类

Interceptor定义如下:


publicinterfaceInterceptorextendsSerializable{

/**
*Calledtoletaninterceptorcleanupanyresourcesithasallocated.
*/
voiddestroy();

/**
*Calledafteraninterceptoriscreated,butbeforeanyrequestsareprocessedusing
*{@link#intercept(com.opensymphony.xwork2.ActionInvocation)intercept},giving
*theInterceptorachancetoinitializeanyneededresources.
*/
voidinit();

/**
*AllowstheInterceptortodosomeprocessingontherequestbeforeand/oraftertherestoftheprocessingofthe
*requestbythe{@linkActionInvocation}ortoshort-circuittheprocessingandjustreturnaStringreturncode.
*
*@paraminvocationtheactioninvocation
*@returnthereturncode,eitherreturnedfrom{@linkActionInvocation#invoke()},orfromtheinterceptoritself.
*@throwsExceptionanysystem-levelerror,asdefinedin{@linkcom.opensymphony.xwork2.Action#execute()}.
*/
Stringintercept(ActionInvocationinvocation)throwsException;

}


我们的拦截器必须重写Interceptor接口中的intercept方法,该方法有一个ActionInvocation的参数,该参数实例可以用于获得被拦截的Action实例。


MyActionaction=(MyAction)invocation.getAction();


一旦获得了Action实例,几乎获得了Action的全部控制权:

可以实现将HTTP请求参数解析出来,设置成Action的属性(这是系统拦截器干的事情)

可以直接将HTTP请求中的HttpServletRequest实例和HttpServletResponse实例传给Action(这是servlet-config拦截器干的事情)

…..

下面是个自定义拦截器类的例子

拦截器类


importcom.jj.test.redirect.RedirectTest;
importcom.opensymphony.xwork2.ActionInvocation;
importcom.opensymphony.xwork2.interceptor.AbstractInterceptor;

publicclassMyInterceptorextendsAbstractInterceptor{
@Override
publicStringintercept(ActionInvocationinvocation)throwsException{
RedirectTestaction=(RedirectTest)invocation.getAction();
System.out.println(action.getTarget());
System.out.println("Start------");
longstart=System.currentTimeMillis();

//执行Action的exceute方法
Stringresult=invocation.invoke();

longend=System.currentTimeMillis();
System.out.println("End------"+(end-start));
returnresult;
}

}




ActionInvocation类常用方法

getAction方法-获取Action实例

getInvocationContext方法-获取ActionContext实例

invoke方法-执行该拦截器的后一个拦截器,或者直接执行Action的execute方法

在intercept方法中获取Session


ActionContextctx=invocation.getInvocationContext();

Mapsession=ctx.getSession();


struts.xml中的配置


<packagename="redir"namespace="/redir"extends="struts-default">
<interceptors>
<interceptorname="MyInterceptor"class="com.jj.test.interceptor.MyInterceptor"></interceptor>
</interceptors>
<default-interceptor-refname="MyInterceptor"/>
<actionname="RedirTest"class="com.jj.test.redirect.RedirectTest">

<resultname="SUCCESS"type="dispatcher">/${target}.jsp</result>
</action>
</package>


8.方法过滤拦截器

在默认情况下,如果为某个Action定义了拦截器,则这个拦截器会拦截该Action的所有方法,有些情况下我们只需要拦截某个Action的某些方法,着就要使用到方法过滤器。

为了实现方法过滤的特性,struts2提供了一个MethodFilterInterceptor类,该类是AbstractInterceptor类的子类,用户可以继承该类来实现自己的方法过滤器类。

MethodFilterInterceptor类重写了AbstractInterceptor类的intercept方法,但提供了一个doIntercept的抽象方法。如果用户需要实现自己的拉涅逻辑,则应该重写doIntercept方法。

MethodFilterInterceptor类中还有两个额外的方法

setExcludeMethods:排除需要过滤的方法,这些方法不会被拦截

setIncludeMethods:要拦截的方法

提示:如果一个方法同时在excludeMethods和includeMethods中列出,则该方法会被拦截。

方法过滤拦截器与普通拦截器使用类似,该拦截器配置举例:

<interceptors>
<interceptorname="MethodFilterTest"class="…"/>
</interceptors>
<action…>
<interceptor-refname="MethodFilterTest">
<paramname="excludeMethods">execute,run</param>
</intercepter-ref>
……
</action

其他Struts2中提供的方法过滤拦截器

TokenInterceptor

TokenSessionStorInterceptor

DefaultWorkflowInterceptor

ValidationInterceptor


9.拦截器的执行顺序

如果一个Action使用了多个拦截器,则这些拦截器的使用顺序如下:

在执行目标Action的execute方法之前
执行第一个拦截器的代码
执行第二个拦截器的代码
……
执行第n个拦截器的代码
在执行目标Action的execute方法之后
执行第n个拦截器的代码
……
执行第二个拦截器的代码
执行第一个拦截器的代码

10.拦截结果的监听器

为了精确定义在execute方法执行结束后,再处理Result执行的动作,Struts2提供了用于拦截结果的监听器,这个监听器必须手动在其他拦截器内注册。

实现拦截结果的监听器首先必须现实com.opensymphony.xwork2.interceptor.PreResultListener类并重写里面的方法beforeResult:


publicclassMyListenerimplementsPreResultListener{

publicvoidbeforeResult(ActionInvocationinvocation,StringresultCode){

System.out.println(resultCode);

}

}


结果监听器的注册(必须手动在其他拦截器内注册)

如在某个拦截器的intercept方法中添加撰写如下代码:


publicStringintercept(ActionInvocationinvocation)throwsException{

//将一个拦截器结果的监听器注册给该拦截器

invocation.addPreResultListener(newMyPreResultListener());

System.out.println("execute()方法执行之前的拦截...");

//调用下一个拦截器,或者Action的执行方法

Stringresult=invocation.invoke();

System.out.println("execute()方法执行后的拦截...");
returnresult;
}



拦截器结果监听器是在系统处理Result之前,在execute之后执行的。

虽然beforeResult方法中也可以获得AcionInvocation实例,但千万不可通过该实例再次调用invoke方法,如果再次调用,将会再次执行Action处理,紧跟着在调用beforeResult方法,造成死循环。

注意:不要在PreResultListener监听器的beforeResult方法中通过ActionInvocation参数调用invoke方法。否则容易造成死循环。

11.拦截器栈中特定拦截器参数的覆盖

若想覆盖某拦截器栈中特定拦截器的某些参数,可以在配置Action的拦截器引用时使用如下形式


<interceptor-refname="interceptor-stack">

<paramname="要覆盖的拦截器名.参数名">参数的值</param>

</interceptor>

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: