`
learnworld
  • 浏览: 169508 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

struts1源码分析(二)初始化主线

阅读更多

Struts1整体概览和核心组件一文中,我们提到了Struts1框架的两条主线:初始化主线和请求处理主线,本文将探寻Struts1框架初始化这条主线。本文使用的Struts版本为1.2.8, 不同版本会略有差异,1.3.x系列对请求处理进行优化,差异性将另文叙述。

 

[问题]

在介绍初始化过程之前,我们先来思考几个问题。

1. 如何在web应用中植入框架的初始化过程?

2. 如何便捷地读取框架配置文件,完成配置文件到Java对象的映射?

3. 哪些组件需要完成初始化,如果完成?

4. 如何存储初始化阶段的成果?

 

[初始化入口]

在上文中我们提到,Struts1框架的入口和核心是ActionServlet类。通常在web.xml中会有如下配置:

<servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
          <param-name>config</param-name>
          <param-value>
              /WEB-INF/struts-config.xml
          </param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*</url-pattern>
</servlet-mapping>

上述配置可以看出,所有的请求都交由ActionServlet进行处理。ActionServlet本身作为一个Servlet(继承HttpServlet),自然符合Servlet的生命周期,容器在调用service()方法处理请求之前,需要提前调用init()方法完成Servlet初始化工作。init()方法在Servlet生命周期中只会被调用一次。由于Servlet生命周期中的这些特性,使得框架的初始化入口一般都在init()方法中。其他框架也不例外,比如SpringMVC,框架初始化交由DispatcherServlet的init()方法完成。

 

[初始化概览]

从整体上先预览一下init()方法:

 

public void init() throws ServletException {

    // 此处省略了方法前后的异常处理
    // 第一阶段,准备阶段
    initInternal();
    initOther();
    initServlet();
    
    // 第二阶段, 默认模块配置解析
    getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
    initModuleConfigFactory();
    // Initialize modules as needed
    ModuleConfig moduleConfig = initModuleConfig("", config);

    // 第三阶段,默认模块组件初始化
    initModuleMessageResources(moduleConfig);
    initModuleDataSources(moduleConfig);
    initModulePlugIns(moduleConfig);
    moduleConfig.freeze();

    // 第四阶段, 自定义模块初始化
    Enumeration names = getServletConfig().getInitParameterNames();
    while (names.hasMoreElements()) {
        String name = (String) names.nextElement();
        if (!name.startsWith("config/")) {
            continue;
        }
        String prefix = name.substring(6);
        moduleConfig = initModuleConfig
            (prefix, getServletConfig().getInitParameter(name));
        initModuleMessageResources(moduleConfig);
        initModuleDataSources(moduleConfig);
        initModulePlugIns(moduleConfig);
        moduleConfig.freeze();
    }

    // 第五阶段,收尾阶段
    this.initModulePrefixes(this.getServletContext());
    this.destroyConfigDigester();

}
 

 

这里将初始化过程分为五个阶段,通过分析每个阶段做了那些事来解读整个初始化过程。下面简要介绍一下每个阶段所做的工作。
第一阶段:准备阶段。这个阶段是为后续工作做准备,如读取Servlet参数信息和准备框架内部用到的工具等。

第二阶段:默认模块配置解析。这个阶段完成默认模块配置文件的解析工作,是整个初始化的核心阶段。

第三阶段:默认模块组件初始化。在第二阶段中,组件的配置信息(MessageResource/DataSource/PlugIn)已经读取,这里需要对各个组件进行进一步加工,完成各个组件的初始化。

第四阶段:自定义模块初始化。自定义模块初始化步骤和默认模块一样,完成配置解析和组件初始化。

第五阶段:收尾阶段。准备框架需要的其他信息和清理初始化过程中使用的辅助工具。

 

下面将对依次对每个阶段的工作做进一步解读。

 

[一、准备阶段]

这阶段主要完成ActionServlet参数读取和框架内部工具准备,总共三个方法。

1. initInternal()

用来初始化框架本身使用的MessageResource。框架自身处理过程中, 可能会出现很多的错误日志信息,这些错误信息需要做国际化,所以框架在启动初期首先完成该操作。框架自身提供的错误信息资源文件路径为org/apache/struts/action/ActionResources.properties,只支持英文和日文两种语言。

 

2. initOther()

从web.xml中读取ActionServlet 的初始化参数"config"和"convertNull"。

1) “config”参数为Struts默认模块的配置信息,包含Struts框架用到的配置文件列表信息。第二阶段将依据该参数值来解析配置文件。

2) "convertNull"参数用于指定FormBean参数转换时Java包装类的初始化默认值。如果设置为true,则使用null作为包装类的默认值。设置初始值为null的方法如下:

if (convertNull) {
    // 1. 注销所有的转换器
    ConvertUtils.deregister();
    // 2. 依次设置各包装类转换器,初始值置为null
    ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
    ConvertUtils.register(new BigIntegerConverter(null), BigInteger.class);
    ConvertUtils.register(new BooleanConverter(null), Boolean.class);
    
    // 此处省略其他包装类转换器         
}

 

3. initServlet()

该方法完成web.xml中ActionServlet信息的读取,包括ServletName和ServletMapping。ServletName信息通过getServletConfig().getServletName()直接获取。ServletMapping信息读取借助Apache Commons Digester Component类库(简称Digester)来完成。Digester类库用途是完成xml文件到Java对象的转换,可以配置规则,针对xml元素设定对应的“Java对象操作”,简化XML->Java对象的转换过程。Digester用户文档参考这里。这里的转换过程主要步骤如下:

1) 创建Digester对象,设定Digester属性值。

2) 在Digester对象中注册框架自带的五个DTD文件。

3) 设置Digester处理web.xml中ServletMapping元素的规则。

4) 执行解析操作,获取ServletMapping的URL pattern,将结果保存到ActionServlet的servletMapping属性中。

 

[二、默认模块配置解析]

这一阶段主要工作是解析Struts配置文件,也就是完成XML配置文件->Java配置类的转换过程。我们先从整体上看一下这个过程中涉及的类图。

1. 类图

Struts配置解析涉及的类图如下:

这里用不同颜色表示不同的功能集合,我们简要介绍一下各集合的功能。

红色(ModuleConfig等):ModuleConfig是配置数据的载体,存储着一个模块需要的所有配置数据。 初始化过程是解析每个模块配置文件的过程,也是构建每个模块对应的ModuleConfig实例的过程。

黄色(ModuleConfigFactory等):ModuleConfig的工厂类,用于构建ModuleConfig实例。

深蓝色(ActionServlet):初始化过程的实际执行者。

浅蓝色(Digester/ConfigRuleSet):解析配置文件使用的辅助类。Digester用于将XML转换为Java对象,ConfigRuleSet指定具体转换规则,既每个XML标签出现后如何操作Java对象。

绿色(ControllerConfig/ActionConfig等):配置文件中各元素对应的JavaBean。比如<Action>元素对应ActionConfig类, <forward>元素对应ForwardConfig类。在使用Digest类解析XML文件过程中,会构建每个元素对应的JavaBean类,最后统一保存在ModuleConfig对象中。

 

看完类图以后,我们来看一下第二阶段的两个方法调用。

 

 2. initModuleConfigFactory()

从web.xml中读取ActionServlet的配置参数configFactory,该参数表示ModuleConfigFacotory的实现类信息,默认工厂实现类为"org.apache.struts.config.impl.DefaultModuleConfigFactory“。如果该参数存在,使用该参数值替换默认工厂实现类。

 

3. initModuleConfig("", config)

非常关键的一步,解析Struts配置文件并生成ModuleConfig对象。该方法的第一个参数是模块的前缀,因为是默认模块,所以前缀为空字符串。第二个参数config是第一阶段中initOther()时读取的配置文件信息。该方法的实现如下:

 

protected ModuleConfig initModuleConfig(String prefix, String paths)
    throws ServletException {

    // 此处省略日志信息

    // 1. 初始化工厂对象并创建ModuleConfig实例
    ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
    ModuleConfig config = factoryObject.createModuleConfig(prefix);

    // 2. 创建Digester对象,用于配置文件解析
    Digester digester = initConfigDigester();

    // 3. 依次完成每个配置文件解析,多个配置文件采用逗号隔开
    while (paths.length() > 0) {
        digester.push(config);
        String path = null;
        int comma = paths.indexOf(',');
        if (comma >= 0) {
            path = paths.substring(0, comma).trim();
            paths = paths.substring(comma + 1);
        } else {
            path = paths.trim();
            paths = "";
        }

        if (path.length() < 1) {
            break;
        }

        this.parseModuleConfigFile(digester, path);
    }

    // 4. 保存解析结果,将结果存入ServletContext中
    getServletContext().setAttribute(
        Globals.MODULE_KEY + config.getPrefix(),
        config);

    // 5. 创建动态Form Bean对应的DynaActionFormClass实例
    FormBeanConfig fbs[] = config.findFormBeanConfigs();
    for (int i = 0; i < fbs.length; i++) {
        if (fbs[i].getDynamic()) {
            fbs[i].getDynaActionFormClass();
        }
    }

    return config;
}
通过上面代码可以看出,整个过程包含五步:

 

1. 创建工厂对象并构建ModuleConfig实例,这里ModuleConfig属性被设置为默认值。

2. 创建Digester对象,用于后续的配置文件解析。在initConfigDigester()方法中,首先构建Digester对象,并设置解析规则,默认解析规则由ConfigRuleSet类指定,用户自定义解析规则也将被加入Digester对象中。

3. 依次完成每个配置文件解析工作。parseModuleConfigFile()读取配置文件信息并交由Digester对象进行解析,解析结果放入ModuleConfig对象中。

4. 将解析结果ModuleConfig放入ServletContext中,采用的key为"Globals.MODULE_KEY+模块前缀"。采用这种方式区分存储不同模块对应的ModuleConfig对象。

5. 根据FormBeanConfig配置,创建动态FormBean对应的DynaActionFormClass对象,用于请求处理过程中生成ActionForm对象。

 

完成以上五步,ModuleConfig对象已生成完毕,第二阶段工作已经完成。

 

[三、默认模块组件初始化]

ModuleConfig对象生成完毕后,框架配置信息已全部读取,第三阶段在这个基础上做进一步处理。这里包含对三个组件的进一步处理。

 

1. initModuleMessageResources(moduleConfig)

完成各MessageResource对象的初始化工作,用于处理资源国际化。关于MessageResource的初始化和使用,将另文详述,这里不做进一步说明。

 

2. initModuleDataSources(moduleConfig)

完成DataSource对象的初始化工作。首先读取配置的DataSource类型信息,生成对应的实例;读取配置文件中DataSource的properties信息,完成DataSource属性设置;将生成的DataSource对象放入dataSources列表中。

 

3. initModulePlugIns(moduleConfig)

完成PlugIn对象的初始化工作。首先读取PlugIn类型信息,生成PlugIn实例;读取配置文件中PlugIn的properties信息,完成PlugIn属性设置;调用PlugIn的init()方法完成初始化工作。

 

4. moduleConfig.freeze()

设置标志位,用来说明模块配置已初始化完毕,后续对ModuleConfig的修改操作将导致异常。

 

完成默认模块组件初始化后,默认模块初始化工作已经全部完毕,接下来将开始自定义模块的初始化工作。

 

[四、自定义模块初始化]

该阶段依次读取每个自定义模块的配置信息,完成各模块的初始化工作。初始化过程和默认模块一致,分为两个阶段:配置解析和组件初始化。如何区分默认模块和自定义模块配置信息? 这里采用模块前缀(prefix)作为模块的标识信息。比如在ServletContext中存储ModuleConfig对象时,默认模块key为"Globals.MODULE_KEY",而自定义模块key为"Globals.MODULE_KEY + 模块前缀“。其他地方都是做类似处理。

 

[五、收尾阶段]

收尾阶段完成其他属性设置和资源清理。主要包含两个方法:

1. initModulePrefixes(this.getServletContext())

将所有模块前缀信息存入ServletContext中,供后续使用。

 

2. destroyConfigDigester()

完成配置文件读取时使用的digester对象清理。

 

[小结]

本文详细分析Struts1框架整个初始化主线,回顾一下关键要点:

1. 初始化入口是借助ActionServlet生命周期中的init()方法完成。

2. 初始化的目标是准备框架使用的各个配置对象和组件,核心是围绕解析配置文件和生成ModuleConfig对象。

3. 整体过程分三个阶段:准备阶段,模块初始化和收尾阶段。模块初始化分为配置解析和组件初始化两个阶段,默认模块和自定义模块都需要经过这两个阶段。

 

通过学习初始化这条主线,我们已了解Struts1框架在启动阶段做了哪些工作,下文将从请求处理主线来解读Struts框架。

 

  • 大小: 75.9 KB
分享到:

相关推荐

    struts2 源码分析

    从 org.apache.struts2.dispatcher.FilterDispatcher 开始 Java 代码阅读,我们可以看到 FilterDispatcher 的 init 方法,它负责初始化 Dispatcher 对象,并创建了一个 ActionMapper 实例,以便对请求进行处理。...

    struts2源码分析总结

    本文将深入探讨Struts2的源码分析,特别是关于StrutsPrepareAndExecuteFilter的初始化过程,这是Struts2的核心组件之一,负责处理HTTP请求。 首先,我们来看`StrutsPrepareAndExecuteFilter`的初始化。这个过滤器...

    struts2源码分析

    struts2源码详细解析51CTO下载-struts2源代码分析(个人觉得非常经典)

    Struts2源码分析

    `Dispatcher`类在Struts2中扮演着重要角色,它负责框架的初始化和Action的执行。`FilterDispatcher`则是一个Servlet Filter,根据配置文件来决定哪些请求需要经过Struts2处理。如果配置不当,可能会导致问题,如将...

    struts 1 源码分析

    下面我们将深入探讨Struts 1的源码分析,特别是针对Struts 1.2版本。 1. **架构概述** - **DispatcherServlet**:作为Struts的核心,DispatcherServlet负责接收HTTP请求并分发到相应的Action。 - **ActionMapping...

    Struts1源码解析

    ActionServlet是Struts1的核心组件,它的生命周期分为初始化、拦截请求和销毁三个阶段。在初始化阶段,`init()`方法执行了一系列关键步骤: 1. `initInternal()`方法初始化内部资源,如国际化设置。它包含了英文和...

    struts2部分源码分析

    Interceptor链是由用户自定义的拦截器和Struts2内置的拦截器组成,例如PrepareInterceptor会确保Action实例被正确初始化。Action执行完成后,Result对象会根据配置的类型和值来决定如何处理返回的结果。 此外,...

    struts1.2源码研究

    ActionServlet的初始化过程是整个框架工作的基础,它负责加载配置、初始化模块并处理请求。通过深入理解Struts 1.2的源码,开发者可以更好地利用这个框架,解决实际项目中的问题,同时为自定义扩展和优化提供可能。

    Struts2源码阅读

    `ActionProxy`创建并初始化`ActionInvocation`对象,`ActionInvocation`则负责执行Action,并管理拦截器(Interceptors)的调用。 在请求流程中,`ActionInvocation`会按照预定义的顺序调用拦截器,然后执行Action...

    探讨Struts的初始化方法

    ### Struts框架初始化详解 #### 一、Struts框架简介 Struts是一个开源的MVC(Model-View-Controller)架构实现,用于简化Java Web应用的开发过程。它基于Servlet和JSP技术来构建Web应用程序,能够帮助开发者更加...

    struts1源码(完整版)

    学习Struts1源码有助于开发者更好地理解Web应用的架构设计,以及MVC模式的实现细节,对于提升Java Web开发技能和解决问题具有很大帮助。同时,虽然Struts1已逐渐被更新的框架如Struts2和Spring MVC取代,但其设计...

    struts2源码最新

    - **StrutsPrepareAndExecuteFilter**:这是Struts2的核心过滤器,负责初始化Struts2框架并处理所有的HTTP请求。 - **ActionContext**:存储了请求期间的上下文信息,包括值栈、session、request和response对象等。...

    struts2源码

    struts2源代码及API帮助文档。...跟读struts2源码的时候注意两条主线:一条是初始化;另外一条是监听http请求。学习初始化过程是如何获取配置构造容器,其中的巧妙构思和设计模式的运用非常值得学习。

    struts2 源码解读

    源码分析是提升编程技能和解决问题的关键,特别是对于复杂的框架如Struts2,理解其内部工作原理能够帮助我们优化应用性能,修复潜在问题,并进行定制化开发。 首先,Struts2的核心组件包括Action、Result、...

    struts2框架源码分析及问题汇总

    3. StrutsPrepareAndExecuteFilter:这是Struts2的过滤器,用于初始化和处理请求。它在web.xml中配置,负责拦截请求并将其交给Struts2的Dispatcher。 4. ActionMapper:ActionMapper根据请求URL映射到相应的Action...

    struts的form不能初始化java.util.Date类型

    然而,有一个常见的问题出现在尝试初始化`java.util.Date`类型的字段时,即“Struts的form不能初始化java.util.Date类型”。这个问题通常是由于日期对象的序列化和反序列化机制导致的,以及Struts默认的数据绑定策略...

    Struts2源码分析--请求处理.pdf

    Struts2源码分析--请求处理.pdf

    struts1源码下载

    11. **ActionServlet的生命周期**:从初始化到服务,再到销毁,理解这一过程有助于优化应用性能。 深入研究Struts1的源码,开发者可以学习到如何优雅地处理HTTP请求、如何组织业务逻辑、如何实现视图和模型的解耦,...

    struts2.1.8 struts2源码 Eclipse关联这个可以

    1. **Struts2的核心概念** - **Action**: Action是业务逻辑的载体,负责处理用户请求并返回结果。在Struts2中,Action可以通过实现`com.opensymphony.xwork2.Action`接口或继承`org.struts2.interceptor栈...

Global site tag (gtag.js) - Google Analytics