深入Struts2_架构与流程

1. Struts2与MVC

  • 1.1 基本的MVC架构

模型-视图-控制器或通常被称为 MVC,是一种用于开发web应用程序的软件设计模式.MVC模式由以下三个部分组成:
Model:模式的最低层,负责维护数据.
View:负责显示全部或部分的数据给用户.
Controller: 控制模型和视图之间的交互的软件代码.
MVC抽象用图形表示如下:

mvc

  • 1.2 Struts2的MVC

Struts2是一个pull-MVC(或 MVC2)框架。Struts 2 的模型-视图-控制器模式由下面的五个核心部件实现:
** 动作,拦截器,值栈/OGNL,结果/结果类型,视图技术**.

mvc2

控制器是由 Struts2调度servlet过滤器和拦截器实现的.
模型是由Action(包括ActionForm)实现的.
视图是由JSp,FreeMaker等技术实现的,抽象成结果.
值栈/OGNL,结果/结果类型提供共同主线,连接和集成其他组件.

2. Struts架构

2

这张图很好地反应了Struts2的整个框架.
FilterDispatcher:整个Struts2的调度中心(现在用StrutsPrepareAndExecuteFilter),也就是MVC中的C(控制中心),根据ActionMapper的结果来决定是否处理请求,如果ActionMapper指出该URL应该被Struts2处理,那么它将会执行Action处理,并停止过滤器链上还没有执行的过滤器.
ActionMapper:会判断这个请求是否应该被Struts2处理,如果需要Struts2处理,ActionMapper会返回一个对象来描述请求对应的ActionInvocation的信息.
ActionProxy:它会创建一个ActionInvocation代理实例,位于Action和xwork之间,使得我们在将来有机会引入更多的实现方式,比如通过WebService来实现等.
ConfigurationManager:是xwork配置的管理中心,通俗的讲,可以把它看做struts.xml这个配置文件在内存中的对应.
struts.xml是Stuts2的应用配置文件,负责诸如URL与Action之间映射的配置、以及执行后页面跳转的Result配置等.
ActionInvocation:真正调用并执行Action,它拥有一个Action实例和这个Action所依赖的拦截器实例.ActionInvocation会执行这些拦截器、Action以及相应的Result.类似于调度器.
Interceptor(拦截器):拦截器是一些无状态的类,拦截器可以自动拦截Action,它们给开发者提供了在Action运行之前或Result运行之后来执行一些功能代码的机会。类似于我们熟悉的javax.servlet.Filter.
Action:动作类是Struts2中的动作执行单元。用来处理用户请求,并封装业务所需要的数据.
Result:Result就是不同视图类型的抽象封装模型,不同的视图类型会对应不同的Result实现,Struts2中支持多种视图类型,比如Jsp,FreeMarker等.
Templates:各种视图类型的页面模板,比如JSP就是一种模板页面技术.
Tag Subsystem:Struts2的标签库,它抽象了三种不同的视图技术JSP、velocity、freemarker,可以在不同的视图技术中,几乎没有差别的使用这些标签.

3. Struts2运行流程

  1. 用户发出请求
    请求会被Tomcat接收到,Tomcat服务器来选择处理这个请求的Web应用,就是选择哪一个web工程来处理这个请求.
  2. web容器去读这个工程的web.xml
    在web.xml中进行匹配,发现是由struts2的过滤器FilterDispatcher(StrutsPrepareAndExecuteFilter)来处理,找到该过滤器的实例(初始化).
  3. 找到FilterDispatcher,回调doFilter()
    通常情况下,web.xml文件中还有其他过滤器时,FilterDispatcher是放在滤器链的最后;如果在FilterDispatcher前出现了如SiteMesh这种特殊的过滤器,还必须在SiteMesh前引用Struts2的ActionContextCleanUp过滤器.
  4. FilterDispatcher将请求转发给ActionMapper
    ActionMapper负责识别当前的请求是否需要Struts2做出处理.
  5. ActionMapper告诉FilterDispatcher,需要处理这个请求,建立ActionProxy
    FilterDispatcher会停止过滤器链以后的部分,所以通常情况下:FilterDispatcher应该出现在过滤器链的最后。然后建立一个ActionProxy对象,这个对象作为Action与xwork之间的中间层,会代理Action的运行过程.
  6. ActionProxy询问ConfigurationManager,读取Struts.xml
    ActionProxy对象刚被创建出来的时候,并不知道要运行哪个Action,它手里只有从FilterDispatcher中拿到的请求的URL.这时候,它问ConfigurationManager问到底要运行哪个Action.
    而ConfigurationManager就是负责读取并管理struts.xml的,可以简单的理解为ConfigurationManager是struts.xml在内存中的映像.
    在服务器启动的时候,ConfigurationManager会一次性的把struts.xml中的所有信息读到内存里,并缓存起来,以保证ActionProxy拿着来访的URL向他询问要运行哪个Action的时候,就可以直接匹配、查找并回答了.
  7. ActionProxy建立ActionInvocation对象
    ActionProxy拿到了运行哪个Action、相关的拦截器以及所有可能使用的result信息,就可以着手建立ActionInvocation对象了,ActionInvocation对象描述了Action运行的整个过程.
  8. 在execute()之前的拦截器
    在execute()之前会执行很多默认的拦截器.拦截器的运行被分成两部分,一部分在Action之前运行,一部分在Result之后运行,而且顺序是刚好反过来的。也就是在Action执行前的顺序,比如是拦截器1、拦截器2、拦截器3,那么运行Result之后,再次运行拦截器的时候,顺序就变成拦截器3、拦截器2、拦截器1了。
  9. 执行execute()方法
  10. 根据execute方法返回的结果,也就是Result,在struts.xml中匹配选择下一个页面
  11. 找到模版页面,根据标签库生成最终页面
  12. 在execute()之后执行的拦截器,和8相反
  13. ActionInvocation对象执行完毕
    这时候已经得到了HttpServletResponse对象了,按照配置定义相反的顺序再经过一次过滤器,向客户端展示结果.

4. 再看Struts2架构与流程

Struts2本身,也包含了真正意义上的Struts2与Xwork两种框架.从职责上来说,XWork才是真正实现MVC的框架,Struts2的工作是在对Http请求进行一定处理后,委托XWork完成真正的逻辑处理.将Web容器与MVC实现分离解耦,是Struts2的精妙之处

  • 4.1 Struts2部分

在第二节的框架图中,过滤器部分均属于Struts2部分.
包括StrutsPrepareFilterStrutsExecuteFilter,是StrutsPrepareAndExecuteFilter的两部分.
前者是Struts2进行Http请求的预处理,而后者是Struts2进行Http请求的逻辑处理.
StrutsPrepareAndExecuteFilter自2.1.3替代FilterDispatcher.如果想自定义过滤器, 要放在strtus2过滤器之前,又想在执行action之前拿filter做一些事,FilterDispatcher做不到.而StrutsPrepareAndExecuteFilter可以拆分成StrutsPrepareFilter和StrutsExecuteFilter,可以在这两个过滤器之间加上自己的过滤器.

  • 4.2 XWork部分

在离开web容器以后,就到了Xwork部分,执行业务逻辑.它包括了ActionProxy,ActionInvocation,Interceptor,Action,ActionContext,ValueStack,Result七个元素.

xwork

该图很直观地描绘了Xwork的整体架构(不包括Dispatcher),包括了第二节架构图中的大部分,我们称之为控制流元素.另外增加了ActionContext与ValueStack这样的数据流元素.
关于控制流元素,在第二节已有相关介绍,接下来看看数据流元素的内容:
ActionContext-数据环境
ActionContext是一个独立的数据结构,其主要作用是为XWork的执行提供数据环境。无论是请求的参数,还是处理的返回值,甚至一些原生的Web容器对象,都被封装于ActionContext的内部,成为了Struts2 / XWork执行时所依赖的数据基础.职责在于数据存储.
ValueStack-数据访问环境
ValueStack本身是一个数据结构,其主要作用是用以对OGNL计算进行扩展。因而,位于ActionContext之中的ValueStack则赋予了ActionContext进行数据计算的功能,从而使得ValueStack自身成为了一个可以进行数据访问的环境.职责在于数据传输.

  • 4.3 Struts2初始化

第二节的图对应当请求发生时候的过程.那么这一整个初始的框架是怎么产生的呢.其实在Struts2框架处理http请求,Xwork框架处理业务逻辑之前,还需要Struts2框架的初始化过程.
StrutsPrepareAndExecuteFilter是整个框架的入口点,在初始化时候调用init()方法.

  public void init(FilterConfig filterConfig) throws ServletException {
       InitOperations init = new InitOperations();//类似一个工具类,包含了一些初始化操作
       Dispatcher dispatcher = null;//Dispatcher:Struts2的核心分发器
       try {
           /**
            * 封装filterConfig,提供了一个便利的方法
            * getInitParameterNames(),将枚举类型的参数转换成Iterator(EnumerationIterator)
            */
           FilterHostConfig config = new FilterHostConfig(filterConfig);
           init.initLogging(config);//初始化日志
           //初始化Dispatcher
           dispatcher = init.initDispatcher(config);
           init.initStaticContentLoader(config, dispatcher);//初始化静态文件加载器

           prepare = new PrepareOperations(dispatcher);//初始化HTTP预处理的操作类
           execute = new ExecuteOperations(dispatcher);//初始化进行HTTP请求处理的逻辑执行操作类
           this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);

           postInit(dispatcher, filterConfig);//回调方法,留作用户拓展
       } finally {
           if (dispatcher != null) {
               dispatcher.cleanUpAfterInit();
           }
           init.cleanup();
       }
   }

可以看到大致的初始化过程分为以下几步:

  1. 封装FilterConfig->FilterHostConfig
  2. 初始化日志操作
  3. 初始化核心分发器Dispatcher
  4. 初始化静态文件加载器,packages,该参数用来配置自动搜寻目录
  5. 初始化PrepareOperations和ExecuteOperations.

其中我们再来看核心分发器Dispatcher的初始化过程:

  public void init() {
      //初始化配置文件管理器
      if (configurationManager == null) {
          //根据name进行对象寻址
          //DEFAULT_BEAN_NAME = "struts"
          //<bean type="org.apache.struts2.dispatcher.DispatcherErrorHandler" name="struts".../>
          //<bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
          configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
      }

      try {
          init_FileManager(); //初始化文件管理器

          // 初始化Struct2的默认配置加载器:
          // org/apache/struts2/default.properties,
          // 如果项目中需要覆盖,可以在classpath里的struts.properties里覆写
          init_DefaultProperties(); // [1]
          //初始化Xml配置加载器:
          // 如struts-default.xml,struts-plugin.xml,struts.xml
          init_TraditionalXmlConfigurations(); // [2]
          //初始化Properties配置加载器
          init_LegacyStrutsProperties(); // [3]
          //初始化用户自定义的配置加载器
          init_CustomConfigurationProviders(); // [5]
          //初始化由web.xml传入的参数
          init_FilterInitParameters() ; // [6]
          //初始化容器内置的对象
          //eg:ObjectFactory,FreemarkerManager....
          init_AliasStandardObjects() ; // [7]
          //创建容器, 初始化并预加载配置
          Container container = init_PreloadConfiguration();
          //对容器进行依赖注入
          container.inject(this);
          //检查对WebLogic的特殊支持
          init_CheckWebLogicWorkaround(container);
          //初始化所有的DispatcherListener
          if (!dispatcherListeners.isEmpty()) {
              for (DispatcherListener l : dispatcherListeners) {
                  l.dispatcherInitialized(this);
              }
          }
          //初始化错误处理器
          errorHandler.init(servletContext);

      } catch (Exception ex) {
          if (LOG.isErrorEnabled())
              LOG.error("Dispatcher initialization failed", ex);
          throw new StrutsException(ex);
      }
  }

这里还可以分为四部:

  1. 初始化ConfigurationManager
  2. 初始化配置加载器.常用的配置文件加载顺序如
    a. default.properties
    b. struts-default.xml
    c. struts-plugin.xml
    d. struts.xml
    e. struts.properties
    f. web.xml
  3. 初始化容器(创建容器,依赖注入)
  4. 额外的初始化工作(初始化WebLogic配置,执行DispatcherListner的配置..)

4.4 再看Struts2流程

根据上面的总结,从一个更高的抽象角度看,我们把流程分为三部分,也即初始化部分,Struts2处理http请求部分,Xwork处理业务逻辑部分.

liu

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

推荐阅读更多精彩内容

  • 概述 什么是Struts2的框架Struts2是Struts1的下一代产品,是在 struts1和WebWork的...
    inke阅读 2,237评论 0 50
  • 1、struts2工作流程 Struts 2框架本身大致可以分为3个部分: 核心控制器FilterDispatch...
    重山杨阅读 1,512评论 0 38
  • action中如何接受页面传过来的参数 第一种情况:(同名参数) 例如:通过页面要把id=1 name=tom a...
    清枫_小天阅读 2,932评论 1 22
  • 本文包括: 1、Struts 2 概述2、Struts 2 快速入门3、Struts 2 的执行流程4、配置 st...
    廖少少阅读 2,946评论 3 13
  • 概述 Struts就是基于mvc模式的框架!(struts其实也是servlet封装,提高开发效率!) Strut...
    奋斗的老王阅读 2,919评论 0 51