首页 > 代码库 > Struts2中被误解的Interceptor

Struts2中被误解的Interceptor

 

    关于Struts2中的Interceptor,可谓是众说纷纭,五花八门,这里罗列一下网上常见的几种说法:

    1、Interceptor的原理是动态代理。(尼玛,坑死人不偿命呀)

    2、Interceptor的原理是责任链模式。(不要有个拦截器链就说是采用了责任链模式好不好)

    3、Interceptor就是AOP。(尼玛,你了解AOP吗?)

    4、Interceptor采用了AOP思想。(这个是对的)

    5、Interceptor采用了AOP思想,所以它就是根据动态代理实现的。(对此我只想说,动态代理可以实现AOP,但是AOP不是动态代理,就算你知道Spring中AOP采用动态代理实现,也不要以偏概全好不好)

 

    我说的对不对,暂不讨论,拿Struts2的源码看看不就OK了吗?

 

Struts2 Interceptor源码分析

 

Interceptor接口说明:

/* An interceptor is a stateless class that follows the interceptor pattern, as * found in {@link  javax.servlet.Filter} and in AOP languages. * Interceptors are objects that dynamically intercept Action invocations. * They provide the developer with the opportunity to define code that can be executed * before and/or after the execution of an action. They also have the ability * to prevent an action from executing. Interceptors provide developers a way to * encapulate common functionality in a re-usable form that can be applied to one or more Actions. * / 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; }
View Code

 

从Interceptor接口的说明中可以了解到: 

1、Interceptor是一个无状态的类,它采用了拦截器模式。同样还有javax.servlet.Filter,以及AOP中的定义。(这是Interceptor的原理)

2、Interceptor是一个动态的拦截Action调用的对象,使用Interceptor可以在Action调用之前或者之后添加功能,它也可以阻止Action的执行。使用拦截器,可以定义一些通用的功能,然后用于多个Action。(这是Interceptor的功能)

接下来看看代码的结构:

 

找一个比较简单的拦截器实现类LoggingInterceptor:

public class LoggingInterceptor extends AbstractInterceptor {    private static final Logger LOG = LoggerFactory.getLogger(LoggingInterceptor.class);    private static final String FINISH_MESSAGE = "Finishing execution stack for action ";    private static final String START_MESSAGE = "Starting execution stack for action ";     @Override    public String intercept(ActionInvocation invocation) throws Exception {// 在action/interceptor调用前添加功能        logMessage(invocation, START_MESSAGE);// 下一个拦截器执行、或者是执行action        String result = invocation.invoke();// 在action/interceptor调用后添加功能        logMessage(invocation, FINISH_MESSAGE);        return result;    }     private void logMessage(ActionInvocation invocation, String baseMessage) {        if (LOG.isInfoEnabled()) {            StringBuilder message = new StringBuilder(baseMessage);            String namespace = invocation.getProxy().getNamespace();             if ((namespace != null) && (namespace.trim().length() > 0)) {                message.append(namespace).append("/");            }             message.append(invocation.getProxy().getActionName());            LOG.info(message.toString());        }    } } 
View Code

再来看看Struts2中ActionInvocation的默认实现:

// 只是拷贝了部分代码,因为这个类比较大public class DefaultActionInvocation implements ActionInvocation {     protected ActionContext invocationContext;    protected Iterator<InterceptorMapping> interceptors;    protected ValueStack stack; protected Result result;    protected Result explicitResult;    protected String resultCode;    protected boolean executed = false; // invoke方法    public String invoke() throws Exception {       String profileKey = "invoke: ";       try {           UtilTimerStack.push(profileKey);           if (executed) {              throw new IllegalStateException("Action has already executed");           }           if (interceptors.hasNext()) {              final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();              String interceptorMsg = "interceptor: " + interceptor.getName();              UtilTimerStack.push(interceptorMsg);              try {                  resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);              }              finally {                  UtilTimerStack.pop(interceptorMsg);              }           }            else {              resultCode = invokeActionOnly();           }           // this is needed because the result will be executed, then control will return to the Interceptor, which will           // // return above and flow through again           if (!executed) {              if (preResultListeners != null) {                  for (Object preResultListener : preResultListeners) {                     PreResultListener listener = (PreResultListener) preResultListener;                     String _profileKey = "preResultListener: ";                     try {                         UtilTimerStack.push(_profileKey);                         listener.beforeResult(this, resultCode);                     }                     finally {                         UtilTimerStack.pop(_profileKey);                     }                  }              } // now execute the result, if we‘re supposed to               if (proxy.getExecuteResult()) {                  executeResult();              }              executed = true;           }           return resultCode;        }        finally {           UtilTimerStack.pop(profileKey);        }     } 
View Code

从 这个类中看出,从功能上确实满足了在action调用前/后附加功能的目的。调用下一个拦截器时,使用的是ActionInvocation.invoke()方法。那么接下来看看DefaultActionInvocation的设计:

把上面的DefaultActionInvocation类再简化一下就是:

public DefaultActionInvocation implements ActionInvocation{

    protected Iterator<InterceptorMapping> interceptors;

    public String invoke(){

       if(interceptors.hasNext()){

           InterceptorMapping mapping=(InterceptorMapping)interceptors.next();

           Interceptor inp=mapping.getInterceptor();

           String resultcode=inp.interceptor(this);

       }

    }

}

 

好了,现在为用UML类图来表示Interceptor的具体实现:

 

 

 

与上次模式Filter时做的类图比较一下:

 

对Interceptor于Filter从设计上进行比较

1、Interceptor接口就与Filter接口一样。

2、ActionInvocation接口与FilterChain接口一样。

3、FilterContext其实只是一个List<Filter>,DefaultFilterChain中包含一FilterContext,其实就是DefaultFilterChain中包含了一个List<Filter>;

DefaultActionInvocation中也包括了一个集合:Iterator<InterceptorMapping>,这样看来这个设计也是一样的。

 

4、在看看执行过程:

Filter执行时:

public void doFilter(request,reponse,filterChain){

    filterChain.doFilter();

}

Interceptor执行时:

public String intercept(ActionInvacation invoker){

    invoker.invoke();

}

 

5、看看如何调用:

FilterChain的实现类中:

doFilter(request, response){

    Filter filter=context.getFilters.get(i);

    filter.doFilter(request, response, this);

}

 

ActionInvocation的实现类中:

invoke(){

iterator.next().getInterceptor.invoke(this);

}

 

写到这个地步了,还觉得是使用的动态代理吗?还觉得是责任链模式吗?既然都把代码说到这个地步了,也就不用在模拟Interceptor了,就是Filter的翻版嘛。

 

既然不是动态代理,不是责任链模式,那是AOP吗?反正官方说是了。

是不是检查一下:

AOP

Advice,Pointcut, join Point, Target, Aspect;

AOP化的过程:

    编译器或运行期,在Target上,找到与PointCut匹配的joinPoint,的前(或/和)后附加上advice。

 

把Interceptor按照AOP过程排一下:

    在运行期,在Action类上,找到与**execute方法匹配的xxExecute(或者execute)方法的前面或者后面添加辅助功能。

    也就是说interceptor的intercept方法中,在调用actionInvocation.invoke()的前后添加的功能就是AOP概念中的advice。

 

所以呢说Interceptor、Filter都是采用了AOP编程思想。并且Interceptor的作用是提供advice(附加功能)。

 

好吧,说到这里了,看看AOP联盟中如何定义Interceptor的:

package org.aopalliance.aop;/** * Tag interface for Advice. Implementations can be any type * of advice, such as Interceptors. * @author Rod Johnson * @version $Id: Advice.java,v 1.1 2004/03/19 17:02:16 johnsonr Exp $ */public interface Advice { }   package org.aopalliance.intercept; /** * This interface represents an invocation in the program. * * <p>An invocation is a joinpoint and can be intercepted by an * interceptor. * * @author Rod Johnson */ public interface Invocation extends Joinpoint {     /**    * Get the arguments as an array object.    * It is possible to change element values within this    * array to change the arguments.    *    * @return the argument of the invocation */   Object[] getArguments(); }

至此,真相大白。