深入Struts2_架构与流程

作者: fredal | 来源:发表于2016-04-25 23:07 被阅读2402次

    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

    相关文章

      网友评论

        本文标题:深入Struts2_架构与流程

        本文链接:https://www.haomeiwen.com/subject/nojqrttx.html