`

Struts2 流程

 
阅读更多
目 录
1.Interceptor结构
2.Interceptor执行分析
3.源码解析
Interceptor结构 
让我们再来回顾一下之前我们曾经用过的一张Action LifeCycle的图:



图中,我们可以发现,Struts2的Interceptor一层一层,把Action包裹在最里面。这样的结构,大概有以下一些特点:

1. 整个结构就如同一个堆栈,除了Action以外,堆栈中的其他元素是Interceptor

2. Action位于堆栈的底部。由于堆栈"先进后出"的特性,如果我们试图把Action拿出来执行,我们必须首先把位于Action上端的Interceptor拿出来执行。这样,整个执行就形成了一个递归调用

3. 每个位于堆栈中的Interceptor,除了需要完成它自身的逻辑,还需要完成一个特殊的执行职责。这个执行职责有3种选择:

1) 中止整个执行,直接返回一个字符串作为resultCode

2) 通过递归调用负责调用堆栈中下一个Interceptor的执行

3) 如果在堆栈内已经不存在任何的Interceptor,调用Action


Struts2的拦截器结构的设计,实际上是一个典型的责任链模式的应用。首先将整个执行划分成若干相同类型的元素,每个元素具备不同的逻辑责任,并将他们纳入到一个链式的数据结构中(我们可以把堆栈结构也看作是一个递归的链式结构),而每个元素又有责任负责链式结构中下一个元素的执行调用。

这样的设计,从代码重构的角度来看,实际上是将一个复杂的系统,分而治之,从而使得每个部分的逻辑能够高度重用并具备高度可扩展性。所以,Interceptor结构实在是Struts2/Xwork设计中的精华之笔。
Interceptor执行分析 
Interceptor的定义

我们来看一下Interceptor的接口的定义:


Java代码
1.public interface Interceptor extends Serializable {  
2. 
3.    /** 
4.     * Called to let an interceptor clean up any resources it has allocated. 
5.     */ 
6.    void destroy();  
7. 
8.    /** 
9.     * Called after an interceptor is created, but before any requests are processed using 
10.     * {@link #intercept(com.opensymphony.xwork2.ActionInvocation) intercept} , giving 
11.     * the Interceptor a chance to initialize any needed resources. 
12.     */ 
13.    void init();  
14. 
15.    /** 
16.     * Allows the Interceptor to do some processing on the request before and/or after the rest of the processing of the 
17.     * request by the {@link ActionInvocation} or to short-circuit the processing and just return a String return code. 
18.     * 
19.     * @return the return code, either returned from {@link ActionInvocation#invoke()}, or from the interceptor itself. 
20.     * @throws Exception any system-level error, as defined in {@link com.opensymphony.xwork2.Action#execute()}. 
21.     */ 
22.    String intercept(ActionInvocation invocation) throws Exception;  
23.} 
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.       *       * @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的接口定义没有什么特别的地方,除了init和destory方法以外,intercept方法是实现整个拦截器机制的核心方法。而它所依赖的参数ActionInvocation则是我们之前章节中曾经提到过的著名的Action调度者。

我们再来看看一个典型的Interceptor的抽象实现类:


Java代码
1.public abstract class AroundInterceptor extends AbstractInterceptor {  
2.      
3.    /* (non-Javadoc) 
4.     * @see com.opensymphony.xwork2.interceptor.AbstractInterceptor#intercept(com.opensymphony.xwork2.ActionInvocation) 
5.     */ 
6.    @Override 
7.    public String intercept(ActionInvocation invocation) throws Exception {  
8.        String result = null;  
9. 
10.        before(invocation);  
11.        // 调用下一个拦截器,如果拦截器不存在,则执行Action  
12.        result = invocation.invoke();  
13.        after(invocation, result);  
14. 
15.        return result;  
16.    }  
17.      
18.    public abstract void before(ActionInvocation invocation) throws Exception;  
19. 
20.    public abstract void after(ActionInvocation invocation, String resultCode) throws Exception;  
21. 
22.} 
public abstract class AroundInterceptor extends AbstractInterceptor {      /* (non-Javadoc)    * @see com.opensymphony.xwork2.interceptor.AbstractInterceptor#intercept(com.opensymphony.xwork2.ActionInvocation)    */   @Override   public String intercept(ActionInvocation invocation) throws Exception {    String result = null;            before(invocation);          // 调用下一个拦截器,如果拦截器不存在,则执行Action          result = invocation.invoke();          after(invocation, result);            return result;   }      public abstract void before(ActionInvocation invocation) throws Exception;     public abstract void after(ActionInvocation invocation, String resultCode) throws Exception;    }

在这个实现类中,实际上已经实现了最简单的拦截器的雏形。或许大家对这样的代码还比较陌生,这没有关系。我在这里需要指出的是一个很重要的方法invocation.invoke()。这是ActionInvocation中的方法,而ActionInvocation是Action调度者,所以这个方法具备以下2层含义:

1. 如果拦截器堆栈中还有其他的Interceptor,那么invocation.invoke()将调用堆栈中下一个Interceptor的执行。

2. 如果拦截器堆栈中只有Action了,那么invocation.invoke()将调用Action执行。

所以,我们可以发现,invocation.invoke()这个方法其实是整个拦截器框架的实现核心。基于这样的实现机制,我们还可以得到下面2个非常重要的推论:

1. 如果在拦截器中,我们不使用invocation.invoke()来完成堆栈中下一个元素的调用,而是直接返回一个字符串作为执行结果,那么整个执行将被中止。

2. 我们可以以invocation.invoke()为界,将拦截器中的代码分成2个部分,在invocation.invoke()之前的代码,将会在Action之前被依次执行,而在invocation.invoke()之后的代码,将会在Action之后被逆序执行。

由此,我们就可以通过invocation.invoke()作为Action代码真正的拦截点,从而实现AOP。

Interceptor拦截类型

从上面的分析,我们知道,整个拦截器的核心部分是invocation.invoke()这个函数的调用位置。事实上,我们也正式根据这句代码的调用位置,来进行拦截类型的区分的。在Struts2中,Interceptor的拦截类型,分成以下三类:

1. before

before拦截,是指在拦截器中定义的代码,它们存在于invocation.invoke()代码执行之前。这些代码,将依照拦截器定义的顺序,顺序执行。

2. after

after拦截,是指在拦截器中定义的代码,它们存在于invocation.invoke()代码执行之后。这些代码,将一招拦截器定义的顺序,逆序执行。

3. PreResultListener

有的时候,before拦截和after拦截对我们来说是不够的,因为我们需要在Action执行完之后,但是还没有回到视图层之前,做一些事情。Struts2同样支持这样的拦截,这种拦截方式,是通过在拦截器中注册一个PreResultListener的接口来实现的。


Java代码
1.public interface PreResultListener {  
2. 
3.    /** 
4.     * This callback method will be called after the Action execution and before the Result execution. 
5.     * 
6.     * @param invocation 
7.     * @param resultCode 
8.     */ 
9.    void beforeResult(ActionInvocation invocation, String resultCode);  
10.} 
public interface PreResultListener {        /**       * This callback method will be called after the Action execution and before the Result execution.       *       * @param invocation       * @param resultCode       */      void beforeResult(ActionInvocation invocation, String resultCode);  } 

在这里,我们看到,Struts2能够支持如此多的拦截类型,与其本身的数据结构和整体设计有很大的关系。正如我在之前的文章中所提到的:


downpour 写道
因为Action是一个普通的Java类,而不是一个Servlet类,完全脱离于Web容器,所以我们就能够更加方便地对Control层进行合理的层次设计,从而抽象出许多公共的逻辑,并将这些逻辑脱离出Action对象本身。


我们可以看到,Struts2对于整个执行的划分,从Interceptor到Action一直到Result,每一层都职责明确。不仅如此,Struts2还为每一个层次之前都设立了恰如其分的插入点。使得整个Action层的扩展性得到了史无前例的提升。

Interceptor执行顺序

Interceptor的执行顺序或许是我们在整个过程中最最关心的部分。根据上面所提到的概念,我们实际上已经能够大致明白了Interceptor的执行机理。我们来看看Struts2的Reference对Interceptor执行顺序的一个形象的例子。

如果我们有一个interceptor-stack的定义如下:


Xml代码
1.<interceptor-stack name="xaStack"> 
2.  <interceptor-ref name="thisWillRunFirstInterceptor"/> 
3.  <interceptor-ref name="thisWillRunNextInterceptor"/> 
4.  <interceptor-ref name="followedByThisInterceptor"/> 
5.  <interceptor-ref name="thisWillRunLastInterceptor"/> 
6.</interceptor-stack> 
<interceptor-stack name="xaStack">    <interceptor-ref name="thisWillRunFirstInterceptor"/>    <interceptor-ref name="thisWillRunNextInterceptor"/>    <interceptor-ref name="followedByThisInterceptor"/>    <interceptor-ref name="thisWillRunLastInterceptor"/>  </interceptor-stack>

那么,整个执行的顺序大概像这样:



在这里,我稍微改了一下Struts2的Reference中的执行顺序示例,使得整个执行顺序更加能够被理解。我们可以看到,递归调用保证了各种各样的拦截类型的执行能够井井有条。

请注意在这里,每个拦截器中的代码的执行顺序,在Action之前,拦截器的执行顺序与堆栈中定义的一致;而在Action和Result之后,拦截器的执行顺序与堆栈中定义的顺序相反。

源码解析 
接下来我们就来看看源码,看看Struts2是如何保证拦截器、Action与Result三者之间的执行顺序的。

之前我曾经提到,ActionInvocation是Struts2中的调度器,所以事实上,这些代码的调度执行,是在ActionInvocation的实现类中完成的,这里,我抽取了DefaultActionInvocation中的invoke()方法,它将向我们展示一切。


Java代码
1./** 
2. * @throws ConfigurationException If no result can be found with the returned code 
3. */ 
4.public String invoke() throws Exception {  
5.    String profileKey = "invoke: ";  
6.    try {  
7.        UtilTimerStack.push(profileKey);  
8.              
9.        if (executed) {  
10.            throw new IllegalStateException("Action has already executed");  
11.        }  
12.        // 依次调用拦截器堆栈中的拦截器代码执行  
13.        if (interceptors.hasNext()) {  
14.            final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();  
15.            UtilTimerStack.profile("interceptor: "+interceptor.getName(),   
16.                    new UtilTimerStack.ProfilingBlock<String>() {  
17.                        public String doProfiling() throws Exception {  
18.                         // 将ActionInvocation作为参数,调用interceptor中的intercept方法执行  
19.                            resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);  
20.                            return null;  
21.                        }  
22.            });  
23.        } else {  
24.            resultCode = invokeActionOnly();  
25.        }  
26. 
27.        // this is needed because the result will be executed, then control will return to the Interceptor, which will  
28.        // return above and flow through again  
29.        if (!executed) {  
30.            // 执行PreResultListener  
31.            if (preResultListeners != null) {  
32.                for (Iterator iterator = preResultListeners.iterator();  
33.                    iterator.hasNext();) {  
34.                    PreResultListener listener = (PreResultListener) iterator.next();  
35.                          
36.                    String _profileKey="preResultListener: ";  
37.                    try {  
38.                            UtilTimerStack.push(_profileKey);  
39.                            listener.beforeResult(this, resultCode);  
40.                    }  
41.                    finally {  
42.                            UtilTimerStack.pop(_profileKey);  
43.                    }  
44.                }  
45.            }  
46. 
47.            // now execute the result, if we're supposed to  
48.            // action与interceptor执行完毕,执行Result  
49.            if (proxy.getExecuteResult()) {  
50.                executeResult();  
51.            }  
52. 
53.            executed = true;  
54.        }  
55. 
56.        return resultCode;  
57.    }  
58.    finally {  
59.        UtilTimerStack.pop(profileKey);  
60.    }  
61.} 
/**   * @throws ConfigurationException If no result can be found with the returned code   */  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();        UtilTimerStack.profile("interceptor: "+interceptor.getName(),           new UtilTimerStack.ProfilingBlock<String>() {        public String doProfiling() throws Exception {                           // 将ActionInvocation作为参数,调用interceptor中的intercept方法执行            resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);            return null;        }        });       } 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) {              // 执行PreResultListener        if (preResultListeners != null) {         for (Iterator iterator = preResultListeners.iterator();          iterator.hasNext();) {          PreResultListener listener = (PreResultListener) iterator.next();                     String _profileKey="preResultListener: ";          try {            UtilTimerStack.push(_profileKey);            listener.beforeResult(this, resultCode);          }          finally {            UtilTimerStack.pop(_profileKey);          }         }        }          // now execute the result, if we're supposed to              // action与interceptor执行完毕,执行Result        if (proxy.getExecuteResult()) {         executeResult();        }          executed = true;       }         return resultCode;      }      finally {       UtilTimerStack.pop(profileKey);      }  } 

从源码中,我们可以看到,我们之前提到的Struts2的Action层的4个不同的层次,在这个方法中都有体现,他们分别是:拦截器(Interceptor)、Action、PreResultListener和Result。在这个方法中,保证了这些层次的有序调用和执行。由此我们也可以看出Struts2在Action层次设计上的众多考虑,每个层次都具备了高度的扩展性和插入点,使得程序员可以在任何喜欢的层次加入自己的实现机制改变Action的行为。

在这里,需要特别强调的,是其中拦截器部分的执行调用:


Java代码
1.resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); 
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);

表面上,它只是执行了拦截器中的intercept方法,如果我们结合拦截器来看,就能看出点端倪来:


Java代码
1.public String intercept(ActionInvocation invocation) throws Exception {  
2.    String result = null;  
3. 
4.        before(invocation);  
5.        // 调用invocation的invoke()方法,在这里形成了递归调用  
6.        result = invocation.invoke();  
7.        after(invocation, result);  
8. 
9.        return result;  
10.} 
public String intercept(ActionInvocation invocation) throws Exception {   String result = null;            before(invocation);          // 调用invocation的invoke()方法,在这里形成了递归调用          result = invocation.invoke();          after(invocation, result);            return result;  }

原来在intercept()方法又对ActionInvocation的invoke()方法进行递归调用,ActionInvocation循环嵌套在intercept()中,一直到语句result = invocation.invoke()执行结束。这样,Interceptor又会按照刚开始执行的逆向顺序依次执行结束。
分享到:
评论

相关推荐

    struts2流程与流程图

    一个请求在Struts 2框架中的处理大概分为以下几个步骤。  客户端提交一个(HttpServletRequest)请求,如上文在浏览器中输入 http://localhost: 8080/bookcode/ch2/Reg.action就是提交一个(HttpServletRequest)...

    struts2工作流程

    NULL 博文链接:https://ollevere.iteye.com/blog/1150639

    Struts2工作流程

    本文件内容包含用EA软件画的Struts2的工作时序图(.eap文件)、对应生成的图片文件(.bmp文件)以及一张经典的Struts2流程图(.png文件)。此文件对理解Struts2的工作原理有极大的帮助。

    Struts2工作流程图

    虽然Struts2号称是一个全新的框架,但这仅仅是相对Struts 1而言。Struts 2 与Struts 1相比,确实有很多革命性的改进,但它并不是新发布的新框架,而是在另一个赫赫有名的框架:WebWork基础上发展起来的。从某种程度...

    Struts2详细分类流程API文档

    Struts2详细地分类Struts2流程、Struts2 API文档等等。。。

    超详细struts2执行流程图

    这张流程图 深刻的描述了 struts2的执行流程 帮助了解Struts2

    struts2建立流程

    Struts 2是Struts的下一代产品,是在 struts 1和WebWork的技术基础上进行了合并的全新的Struts 2框架。其全新的Struts 2的体系结构与Struts 1的体系结构差别巨大。Struts 2以WebWork为核心,采用拦截器的机制来处理...

    Struts2执行流程

    Struts2执行流程 1. web.xml 部署描述符 2. FilterDispatcher 实现StrutsStatics, Filter接口 (1)Filter:一个filter是一个对象用于执行过滤任务为每个请求资源(一个servlet或静态内容),或响应一个资源,或两者.过滤...

    Struts2 基本流程

    我们已在前面学习了Servlet 数据库应用,有了JSP、Servlet 、JDBC的一些知识、理解和...2. Struts2基本流程 3. 了解核心控制器FilterDispatcher及在web.xml中的配置 4. 了解业务控制器Action及在struts.xml中的配置

    Struts2的处理流程

    Struts2的处理流程

    Struts2 运行流程分析

    Struts2 运行流程分析,Struts2 运行流程分析,Struts2 运行流程分析

    Struts运行流程图(详细)

    底层,详细. Struts运行流程图(详细) Struts运行流程图(详细) Struts运行流程图(详细)

    struts2框架处理流程

    包含了struts2的处理流程以及struts2的配置文件,并附有处理流程显示图,更加清楚直观。

    Struts工作流程

    Struts工作流程

    struts业务流程图

    非常明确的,清晰明了的struts 业务流程图,把图看懂了就懂struts了。

    Struts 原理 流程图

    Struts 原理 流程图Struts 原理 流程图

    struts2开发流程简图

    struts2 开发流程 简图 struts2 开发流程 简图 struts2 开发流程 简图 struts2 开发流程 简图

    最详细的Struts工作流程图

    这是一个详细的Struts工作流程图,里面包含JSP、java(Action、ActionForm等)类

Global site tag (gtag.js) - Google Analytics