螺 丝 钉 阅读(161) 评论(0)

 

    关于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();

 

}

至此,真相大白。