Struts 2的拦截器

1. 拦截器的意义

  •     避免重复代码调用

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

  •     实现高层次的代码解耦

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

2. Struts 2内建拦截器

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

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

3.拦截器的实现原理

  定义接口

public interface Dog {
	public void info();
	public void run();
}

定义接口实现类

public class DogImpl implements Dog {

	public void info() {
		...
	} 
	
	public void run() {
		...
	} 
}

系统拦截器类

public class DogIntercepter {
     public void method1(){}
     public void method2(){}
}

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

 
public class ProxyHandler implemts InvocationHandler {
	private Object target;//需被代理的目标对象
	
	//创建拦截器实例
	DogIntercepter di = new DogIntercepter();
	//执行代理的目标方法时,该Invoke方法会被自动调用
	public Object invoke(Object proxy, Method method, Object[] args) {
		Object result = null;
		if(method.getName().equals("info"){
			di.methord1();
			result = method.invoke(tart,args);
			di.method2();
		}
		else {
			result = methos.invoke(target,args);
		}
		return result;
	}
		
	public void setTarget(Object o){
		this.target=o;
	}
}

代理工厂类

public class MYProxyFactory{
	public static Object getProxy(Object object){
		ProxyHandler handler = new ProxyHandler();
		handler.setTarget(object);
		return Proxy.newProxyInstance(Dogimpl.class.getClassLoader(),
object.getClass().getInterfaces(),handler);
	}
}

主程序

public class Test{
	public static void main(String[] args){
		Dog targetObject = new DogImpl();
		Dog dog = null;

		//以目标对象创建代理
		Object proxy = MyProxyFactory.getProxy(targetObject);
		if(proxy instance of Dog) {
			dog=(Dog)proxy;
		}
		
		dog.info();
		dog.run();
	}
}

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

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

  • 在Struts.xml中配置拦截器

<interceptor name="name" class="class"/>

还可以指定拦截器参数

<interceptor name="name" class="class">

      <param name="param name">…</param>

</interceptor>

  • 在Struts.xml中配置拦截器栈

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

<interceptor-stack name="stack name">

      <interceptor-ref name="interceptor 1"/>

      <interceptor-ref name="interceptor 2"/>

      <interceptor-ref name="interceptor stack 1"/>

</interceptor>

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

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

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

<action name="..."   ...>
	......
	<!-- interceptor 1 -->
	<interceptor-ref name="int1"/>
	<!-- interceptor 2 -->
	<interceptor-ref name="int2">
		<param name="...">...</param>
	</nterceptor-ref>
</action
注:系统为拦截器指定参数有2个时机:
  1. 定义拦截器时:即在<interceptor>和<interceptor-stack>中增加<param>子元素
  2. 使用拦截器时:即在<interceptor-ref>中增加<param>子元素

6. 默认拦截器

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

<package name="Test" extends="struts-default">

    <default-interceptor-ref name="defaultStack" />

    <interceptors>

        <interceptor…/>

     </interceptors>

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

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

7.使用自定义拦截器

实现拦截器类

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

  1. 实现com.opensymphony.xwork2.interceptor.Interceptor接口
  2. 继承AbstractInterceptor类

Interceptor定义如下:

public interface Interceptor extends Serializable {

    /**
     * Called to let an interceptor clean up any resources it has allocated.
     */
    void destroy();

    /**
     * Called after an interceptor is created, but before any requests are processed using
     * {@link #intercept(com.opensymphony.xwork2.ActionInvocation) intercept} , giving
     * the Interceptor a chance to initialize any needed resources.
     */
    void init();

    /**
     * Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the
     * request by the {@link ActionInvocation} or to short-circuit the processing and just return a String return code.
     *
     * @param invocation the action invocation
     * @return the return code, either returned from {@link ActionInvocation#invoke()}, or from the interceptor itself.
     * @throws Exception any system-level error, as defined in {@link com.opensymphony.xwork2.Action#execute()}.
     */
    String intercept(ActionInvocation invocation) throws Exception;

}

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

MyAction action = (MyAction)invocation.getAction();

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

  1. 可以实现将HTTP请求参数解析出来,设置成Action的属性(这是系统拦截器干的事情)
  2. 可以直接将HTTP请求中的HttpServletRequest实例和HttpServletResponse实例传给Action(这是servlet-config拦截器干的事情)
  3. …..

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

拦截器类

import com.jj.test.redirect.RedirectTest;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

public class MyInterceptor extends AbstractInterceptor{    
    @Override
    public String intercept(ActionInvocation invocation) throws Exception {
        RedirectTest action = (RedirectTest)invocation.getAction();
        System.out.println(action.getTarget());
        System.out.println("Start------");
        long start = System.currentTimeMillis();
        
        //执行Action的exceute方法
        String result = invocation.invoke();
        
        long end = System.currentTimeMillis();
        System.out.println("End------"+(end-start));
        return result;
    }

}

ActionInvocation 类常用方法

  • getAction方法 -获取Action实例
  • getInvocationContext方法 - 获取ActionContext实例
  • invoke方法 - 执行该拦截器的后一个拦截器,或者直接执行Action的execute方法

在intercept方法中获取Session

ActionContext ctx = invocation.getInvocationContext();

Map session = ctx.getSession();

struts.xml中的配置

<package name="redir" namespace="/redir" extends="struts-default">
        <interceptors>
            <interceptor name="MyInterceptor" class="com.jj.test.interceptor.MyInterceptor"></interceptor>
        </interceptors>
        <default-interceptor-ref name="MyInterceptor"/>
        <action name="RedirTest" class="com.jj.test.redirect.RedirectTest">
        
            <result name="SUCCESS" type="dispatcher">/${target}.jsp</result>
        </action>
 </package>

8. 方法过滤拦截器

在默认情况下,如果为某个Action定义了拦截器,则这个拦截器会拦截该Action的所有方法,有些情况下我们只需要拦截某个Action的某些方法,着就要使用到方法过滤器。
 
为了实现方法过滤的特性,struts2提供了一个MethodFilterInterceptor类,该类是AbstractInterceptor类的子类,用户可以继承该类来实现自己的方法过滤器类。
 
MethodFilterInterceptor类重写了AbstractInterceptor类的intercept方法,但提供了一个doIntercept的抽象方法。如果用户需要实现自己的拉涅逻辑,则应该重写doIntercept方法。
 
MethodFilterInterceptor类中还有两个额外的方法
  1. setExcludeMethods:排除需要过滤的方法,这些方法不会被拦截
  2. setIncludeMethods:要拦截的方法
提示:如果一个方法同时在excludeMethods和includeMethods中列出,则该方法会被拦截。
 
方法过滤拦截器与普通拦截器使用类似,该拦截器配置举例:
<interceptors>
   <interceptor name="MethodFilterTest" class="…" />
</interceptors>
<action …>
   <interceptor-ref name="MethodFilterTest">
      <param name = "excludeMethods">execute,run</param>
   </intercepter-ref>
   ……
</action
其他Struts 2中提供的方法过滤拦截器
  • TokenInterceptor
  • TokenSessionStorInterceptor
  • DefaultWorkflowInterceptor
  • ValidationInterceptor
 

9. 拦截器的执行顺序

如果一个Action使用了多个拦截器,则这些拦截器的使用顺序如下:
在执行目标Action的execute方法之前
执行第一个拦截器的代码
执行第二个拦截器的代码
     ……
执行第n个拦截器的代码
在执行目标Action的execute方法之后
执行第n个拦截器的代码
      ……
执行第二个拦截器的代码
执行第一个拦截器的代码

10. 拦截结果的监听器

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

 

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

 

public class MyListener implements PreResultListener {   

public void beforeResult(ActionInvocation invocation, String resultCode) {   

        System.out.println(resultCode);   

    }   

}

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

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

public String intercept(ActionInvocation invocation) throws Exception {   

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

        invocation.addPreResultListener(new MyPreResultListener());

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



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

        String result = invocation.invoke();

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

 

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

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

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

 

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

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

<interceptor-ref name="interceptor-stack">

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

</interceptor>

posted on 2011-02-12 17:55  Eason Jiang  阅读(1071)  评论(0编辑  收藏  举报

导航