Struts 2的拦截器

September 19, 2011 | tags    | views
Comments 0

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
<strong>注:系统为拦截器指定参数有2个时机:</strong>
定义拦截器时:即在<interceptor>和<interceptor-stack>中增加<param>子元素
使用拦截器时:即在<interceptor-ref>中增加<param>子元素
6. 默认拦截器
配置每一个包时,可以为其指定默认拦截器。如果该包的某个Action未指定拦截器,系统会使用默认拦截器,但是一旦这个Action指定了拦截器则该包的默认拦截器将<strong>不起作用</strong>
配置默认拦截器使用<default-interceptor-ref …/>元素,该元素作为<packag…/>的子元素。
<strong>注:每个包只能有一个<default-interceptor-ref …/>子元素,即每个包至多只能有一个默认拦截器。</strong>
<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.使用自定义拦截器
实现拦截器类
如果用户要开发自己的拦截器类,有如下两种方式:
实现com.opensymphony.xwork2.interceptor.Interceptor接口
继承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的全部控制权:
可以实现将HTTP请求参数解析出来,设置成Action的属性(这是系统拦截器干的事情)
可以直接将HTTP请求中的HttpServletRequest实例和HttpServletResponse实例传给Action(这是servlet-config拦截器干的事情)
…..
下面是个自定义拦截器类的例子
拦截器类
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类中还有两个额外的方法
setExcludeMethods:排除需要过滤的方法,这些方法不会被拦截
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使用了多个拦截器,则这些拦截器的使用顺序如下:
<strong>在执行目标Action的execute方法之前</strong>
执行第一个拦截器的代码
执行第二个拦截器的代码
 
执行第n个拦截器的代码
<strong>在执行目标Action的execute方法之后</strong>
执行第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>


    相关文章:


文章本月排行 文章本年排行
   



发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。