Struts2系列:(16)Interceptor组成的链是如何进行调用的

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Struts2系列:(16)Interceptor组成的链是如何进行调用的相关的知识,希望对你有一定的参考价值。

首先看一下com.opensymphony.xwork2.interceptor.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;

}

在这里,重点关注intercept方法

String intercept(ActionInvocation invocation)

它接收一个ActionInvocation类型的参数。


接下来,进行看一下com.opensymphony.xwork2.ActionInvocation的invoke方法。



com.opensymphony.xwork2.ActionInvocation是一个接口,它提供了invoke方法的定义:

通过invoke方法的注释,可以得到:如果存在多个Interceptor,通过调用ActionInvocation对象的invoke方法,可以让下一个拦截器(Interceptor)执行。

/**
 * ActionInvocation代表了Action的执行状态。由ActionInvocation对象可以获取到拦截器(Interceptors)和Action实例。
 * An ActionInvocation represents the execution state of an Action. It holds the Interceptors and the Action instance.
 * By repeated re-entrant execution of the <code>invoke()</code> method, initially by the {@link ActionProxy}, then by the Interceptors, the
 * Interceptors are all executed, and then the {@link Action} and the {@link Result}.
 *
 * @author Jason Carreira
 * @see com.opensymphony.xwork2.ActionProxy
 */
public interface ActionInvocation extends Serializable {
    /**
     * Invokes the next step in processing this ActionInvocation.
     * 
     * If there are more Interceptors, this will call the next one. If Interceptors choose not to short-circuit
     * ActionInvocation processing and return their own return code, they will call invoke() to allow the next Interceptor
     * to execute. If there are no more Interceptors to be applied, the Action is executed.
     * If the {@link ActionProxy#getExecuteResult()} method returns <tt>true</tt>, the Result is also executed.
     *
     * @throws Exception can be thrown.
     * @return the return code.
     */
    String invoke() throws Exception;

	//其它代码省略
}


在这里,只是对invoke()方法的定义和简单描述,我们需要得到更详尽的信息来知道它是如何执行的。


接下来,我们看com.opensymphony.xwork2.DefaultActionInvocation这个类,它是ActionInvocation接口的默认实现类。




在继续深入下一步的代码之前,让我们回顾一下自己的目的,我们的目的是想要知道:多个Interceptor是如何一个接一个的执行的?在Struts2的内部,一定是维护一个Interceptor的列表,然后一个接一个地执行每一个Interceptor。


接下来的代码对com.opensymphony.xwork2.DefaultActionInvocation进行简化,如下:

public class DefaultActionInvocation implements ActionInvocation {

    protected ActionProxy proxy;
    protected Iterator<InterceptorMapping> interceptors;
    protected String resultCode;

    protected void createInterceptors(ActionProxy proxy) {
        // get a new List so we don‘t get problems with the iterator if someone changes the list
        List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
        interceptors = interceptorList.iterator();
    }


    public String invoke() throws Exception {

		if (interceptors.hasNext()) {
			final InterceptorMapping interceptor = interceptors.next();
			String interceptorMsg = "interceptor: " + interceptor.getName();
			resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
		} else {
			resultCode = invokeActionOnly();
		}

		return resultCode;
    }

}

在上面的createInterceptors(ActionProxy proxy)方法中,看到下面这一行代码:

List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());

我们关注new ArrayList<InterceptorMapping>后面小括号中的代码:

proxy.getConfig().getInterceptors()

我们自己重新写一下这个代码,如下:

		ActionProxy proxy = invocation.getProxy();//ActionInvocation--->ActionProxy
		ActionConfig config = proxy.getConfig();//ActionProxy-->ActionConfig
		List<InterceptorMapping> interceptors = config.getInterceptors();//ActionConfig-->得到所有的拦截器

由ActionInvocation对象可以得到ActionProxy对象,再由ActionProxy对象得到ActionConfig对象,最后由ActionConfig对象得到拦截器的List列表。


接下来,我们再回到createInterceptors(ActionProxy proxy)方法内,

        List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
        interceptors = interceptorList.iterator();

这两句代码执行过后,interceptors是一个迭代器对象。


看完了createInterceptors(ActionProxy proxy)方法,我们再来看invoke()方法

    public String invoke() throws Exception {

		if (interceptors.hasNext()) {
			final InterceptorMapping interceptor = interceptors.next();
			String interceptorMsg = "interceptor: " + interceptor.getName();
			resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
		} else {
			resultCode = invokeActionOnly();
		}

		return resultCode;
    }


通过

interceptors.hasNext()

来查看,是否有后续的元素。如果没有后续元素,则执行

resultCode = invokeActionOnly();

也就是执行最后的action。


如果有后续元素,则通过

final InterceptorMapping interceptor = interceptors.next();

来得到下一个interceptor对象,

resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);

我们再进一步拆解一下代码,就变成 如下代码:

		ActionProxy proxy = invocation.getProxy();//ActionInvocation--->ActionProxy
		ActionConfig config = proxy.getConfig();//ActionProxy-->ActionConfig
		List<InterceptorMapping> interceptors = config.getInterceptors();//ActionConfig-->得到所有的拦截器
		Iterator<InterceptorMapping> iterator = interceptors.iterator();
		if(iterator.hasNext())
		{
			InterceptorMapping interceptorMapping = iterator.next();
			Interceptor interceptor = interceptorMapping.getInterceptor();
			interceptor.intercept(invocation);
		}


resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);

代码,我觉得,最精妙的的地方在于

DefaultActionInvocation.this

"类名.this",这种写法是,当内部类访问外部类的实例时所采用的写法。

在这里,它把自身作为参数传递进行,就使得所有的拦截器的intercept的方法中所接收的ActionInvocation对象是同一个对象。


正因为所有拦截器的intercept方法所接收的ActionInvocation是同一个对象,才使得所有的拦截器能够从第1个逐步迭代到最后一个。

以上是关于Struts2系列:(16)Interceptor组成的链是如何进行调用的的主要内容,如果未能解决你的问题,请参考以下文章

springBoot系列教程08:拦截器(Interceptor)的使用

Struts2系列:(11)文件上传

struts2—过滤器interceptor

struts2中啥时候使用filter啥时候使用interceptor?

Struts2之Struts2-2.5.5 Interceptor

Struts2学习:interceptor(拦截器)的使用