深入struts2_控制流体系

1. Action

  • 1.1 请求相应模式

请求-响应模式是一种概念非常宽泛的人机交互模式,是人与计算机进行沟通的一种最基本的行为方式.
那么在web应用开发过程中都是如何实现的呢.我们会有三种不同的实现方式.
参数-返回值
这是一种最为直观的请求-响应的实现模式.方法参数对应请求内容,返回值对应结果响应.
参数-参数
最底层的Servlet就是基于此模式设计的,而实际上底层规范都不得不采用这样的实现机制.
PoJo
这也就是struts2所采用的请求相应模式了,所有的请求对象都以内部属性变量的方式存在,而且所有的返回值同样是以该形式存在.相当于,POJO对某一次的响应是一个有状态的响应.

  • 2. struts2与springMVC的解决方案

springMVC采用了一种游离于参数-参数模式与参数-返回值模式之间,并整合了各自优势的实现模式,而struts2则是POJO模式.从三个方面比较各自优劣:
请求参数:在这一点上参数比属性变量更为直观,更符合java原生的语法含义.springMVC略胜一筹.
响应数据:对于纯粹的响应数据,最适合的仍然是方法的返回值.然而响应必须包含响应数据以及响应跳转逻辑,而框架的困境在于很难同时表达数据和控制两个方面.SpringMVC使用一个ModelAndView的对象将不同的逻辑处理分开,使用返回值来表达响应流程控制,而为响应方法增加一个Model对象,用以存储数据.Struts2就简单很多,仍然可以用内部属性存储数据.
所以这个方面很难讲孰优孰劣.
响应控制:在上面讲了,springMVC使用ModelAndView处理数据和控制,而struts2直接用返回值处理响应控制即可.
总得来说,在数据访问上struts2有天然的优势.

  • 3. action的突破

回到action本身,action实现了对servlet的两大突破.
与web容器无关
关于这一点,显而易见我们看不到任何和web容器有关的对象.action只是一个普通的java对象.这使得action非常易于测试,看上去更像一个POJO,易于管理,并且使得struts2在控制器这个层面上有更多的发挥余地.
响应对象有状态
action对象表现出两种截然不同的特性,即属性特征与行为特征.从语法角度讲,前者指变量的属性,而后者指的是响应的过程.action是一个状态与动作的结合体,天然符合了响应类的基本要素.action的状态自然解毒为请求数据与响应数据.

2. Interceptor

  • 1.AOP编程

Interceptor本身就是属于AOP的概念,基本实质为一个代码段,通过定义切入点,来指定Interceptor的代码逻辑在切入点之前或之后执行,从而起到"拦截"的作用.
暂且不去说AOP的基本概念的含义,但我们可以看到struts2中的Interceptor与AOP之间的对应
切面Aspect:Interceptor实现
通知Advice:Around通知
切入点Pointcut:Action对象
连接点Joinpoint:Action执行

  • 2. 内部结构与执行方式

对于Interceptor的内部结构,有几点讲
群居:Interceptor对象是一个群居对象,在同一时刻总有多个对象协同工作.
栈结构:这是一个典型的栈结构,而Action位于栈的底部,由于后入先出,所以action才最后执行.那么也许有疑问,不是action之后还要倒着再来一遍Interceptor么.其实这里是用递归调用完成的一个巧妙数据结构.
关于执行方式,来看其一个实现类的源码

  public String intercept(ActionInvocation invocation) throws Exception{
      return invocation.invoke();   
  }

intercept方法以ActionInvocation作为参数,我们知道它是XWork控制流元素的核心调度元素,以它为参数方便自身与其他元素的交互,其次也便于ActionInvocation对自己的调度.
再来看方法的主体,只有一句invocation.invoke().这里有两个含义,其一是有责任把执行的控制权交给栈的下一个元素去执行.其二是如果返回一个String类型的Result就中止栈的调度.

  • 3. AOP还是IOC

Spring是AOP的一个典范,可以以一句话总结:Spring是用IOC实现AOP的.
那么回到XWork的Interceptor上,它的AOP实现与Spring的差别在于对于切入点的定于不同.Spring侧重于方法级别的AOP拦截,而XWork针对Action对象的拦截.
拦截Action对象之后干嘛呢,对其内部属性变量进行管理,影响其运行状态的改变,从而对其行为进行扩展.
换个角度看,内部属性与方法有啥用,构成Action这个响应类与外部环境的交互接口,构成请求数据与响应数据,构成自身行为动作与业务逻辑处理对象之间的交互协作.而这正是依赖关系的体现(环境依赖,数据依赖,协作依赖).
也就是XWork使用AOP实现IOC的.

  • 4. Interceptor的意义

可以从不同的角度看到Interceptor存在的价值.
Interceptor对象的引入,是对事件处理过程中主次职责的有效划分
Interceptor对象的引入能丰富整个事件处理流程的执行层次,为AOP编程打下结实的数据结构基础
Interceptor使得责任链模式在整个执行栈的调度过程中顺利实施

3. ActionInvocation

ActionInvocation是控制流的核心调度元素.
来看其源码

  public interface ActionInvocation extends Serializable{
   Object getAction();
   boolean isExecuted();
   ActionContext getInvocationContext();
   ActionProxy getProxy();
   Result getResult() throws Exception;
   String getResultCode();
   void setResultCode(String resultCode);
   ValueStack getStack();
   void addPreResultListener(PreResultListener listener);
   String invoke() throws Exception;
   String invokeActionOnly() throws Exception;
   void setActionEventListener(ActionEventListener listener);
   void init(ActionProxy proxy) ;
 }

大致可以根据接口方法的不同作用进行逻辑分类
对控制流元素和数据流元素的访问接口
getAction,getActionProxy,getStack等等
对执行调度流程的扩展接口
addPreListner,setActionEventListner等等
对执行栈进行不调度执行的接口
invoke,invokeActionOnly;

  • 3.1 调度分析

ActionInvocation的核心调度功能是通过invoke方法实现,对Interceptor和Action对象共同构成的执行栈进行逻辑执行调度.
看看源码中的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);
       }
   }

我们可以看到如果有下一个拦截器,会不断交给下一个执行,如果没有拦截器了就执行action,,最后执行result的对象逻辑.那么action执行完毕之后倒序的拦截器去哪了呢?
这里就要说递归的巧妙运用,以invocation.invoke为分水岭,递归使得分水岭之后的代码被暂时封存,而去执行栈的下一个元素,等完整的递归调用结束之后,再触发整个逆序的执行过程.

1

4. ActionProxy

ActionProxy是Xwork事件处理框架的总代理

 public interface ActionProxy {

   Object getAction();

   String getActionName();

   ActionConfig getConfig();

   void setExecuteResult(boolean executeResult);

   boolean getExecuteResult();

   ActionInvocation getInvocation();

   String getNamespace();

   String execute() throws Exception;
   String getMethod();

   boolean isMethodSpecified();   
}

看源码可知,ActionProxy也是一个复杂的接口,身处比ActionInvocation更高的层次,主要职责是维护Xwork执行元素与请求对象直接的映射关系.
再来看ActionProxy的初始化,同样是由工厂类实现,在工厂类接口中找到createActionProxy方法

 public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext);

可以看到需要的参数包括
配置关系映射:namespace,actionName,methodName等等..
运行上下文环境:extraContext,cleanupContext

5. 数据流与控制流的交互

2

这张图在讲架构时候类似地出现过.而这里着重于数据流与控制流的交互上.
首先ActionContext横跨了整个Xwork的控制流架构,意味着它的生命周期有那么长.由于ActionContext使用Threadlocal模式实现,也表明它甚至对整个线程都是有效的.
再来看ValueStack,它环绕着Action存在,可以说action是数据流与控制流的交互点.看源码便能一目了然

 public void init(ActionProxy proxy){
  if(pushAction){
     stack.push(action);
     contextMap.put("action",action);
  }
 }

而action的交互体系分为三部分即外部环境交互体系,数据交互体系,协作交互体系.
第一个最主要指action与web容器中对象的交互过程.一方面可以使用servletActionContext实现,另一方面也可以使用ServletConfigInterceptor等拦截器通过IOC注入实现.
第二个就是指与数据流元素的交互了,请求数据,响应数据与属性变量的映射关系,主要由ParametersInterceptor拦截器完成.
第三个指将struts2看成一个门户,获得业务逻辑操作对象的过程,例如与Spring的整合由ActionAutowiringInterceptor完成.

6. result

作为控制流元素的最后一个,result是非常特殊的.一方面接口定义在XWork体系中,但实现类确应算Struts2体系之中,它告诉程序该何去何从.
action似乎把HttpServletResponse的职责都承包了,接口与实现的分离实现了框架级别的解耦合,为control层与view层架起桥梁.
如果说action本身并不实现解耦,而是通过数据流或者AOP拦截器等手段加以实现,那么result本身就是起到了解耦的作用.
仔细思考result的职责,主要有三个:
起到一个中转作用,实现控制权的转移:即从Xwork到Struts2的转移
需要进行两大框架的解耦:从struts2的角度看,这是一个典型的策略模式.
需要一个web容器的代理操作对象:屏蔽底层的细节.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,924评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,781评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,813评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,264评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,273评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,383评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,800评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,482评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,673评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,497评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,545评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,240评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,802评论 3 304
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,866评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,101评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,673评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,245评论 2 341

推荐阅读更多精彩内容