`
learnworld
  • 浏览: 170337 次
  • 性别: 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 技术内幕——深入解析struts2架构设计

    《Struts2技术内幕:深入解析Struts2架构设计与实现原理》由国内极为资深的...运行主线篇首先对Struts2的两大运行主线——初始化主线和HTTP请求处理主线进行了深入的剖析,然后对Struts2的扩展机制进行了解读和抽象。

    action源码java-actions:行动

    深入到具体的源码分析,Action类通常会包含以下组成部分: 1. **属性**:存储与Action相关联的数据,可能是从请求中获取的参数或用于业务处理的中间状态。 2. **构造函数**:初始化Action对象,可能用于设置依赖...

    计算机发展与计算机应用概述.pdf

    计算机发展与计算机应用概述.pdf

    计算机二级公共基础知识全集合.pdf

    计算机二级公共基础知识全集合.pdf

    计算机机试答案.pdf

    计算机机试答案.pdf

    基于STM32F103的750W全桥逆变器并离网设计方案及其实现

    内容概要:本文详细介绍了基于STM32F103RCT6的750W全桥逆变器设计方案,涵盖硬件电路设计、软件编程以及保护机制等方面。硬件部分包括主控芯片的选择、PWM配置、Boost升压电路、PCB布局优化等;软件部分涉及并离网切换的状态机设计、过流保护、风扇控制算法、并机功能实现等。文中还分享了许多实战经验和调试技巧,如死区时间配置、电流采样方法、并网同步算法等。 适合人群:具有一定电子电路和嵌入式开发基础的技术人员,尤其是从事逆变器及相关电力电子产品开发的工程师。 使用场景及目标:适用于希望深入了解逆变器工作原理和技术实现的开发者,特别是那些需要掌握并离网切换、高效电源管理及可靠保护机制的人群。目标是帮助读者构建一个稳定可靠的逆变器系统,能够应对各种复杂的工作环境。 其他说明:本文不仅提供了详细的理论讲解,还有丰富的代码片段和实践经验分享,有助于读者更好地理解和应用相关技术。

    基于Simulink的单相全桥逆变器仿真与优化:MATLAB环境下的详细实现

    内容概要:本文详细介绍了如何利用Simulink在MATLAB环境中搭建单相全桥逆变器的仿真模型。首先,通过构建H桥结构,连接直流电源和RL负载,并引入PWM控制器进行开关管的控制。接着,针对仿真过程中遇到的各种问题,如谐波失真、开关管直通等问题,提出了具体的解决方案,包括加入LC滤波器、设置死区时间和优化PWM参数等。此外,还探讨了通过MATLAB脚本自动化测试不同参数组合的方法,以及如何提高电压利用率和降低谐波失真。最终,通过对仿真结果的分析,验证了所提方法的有效性和优越性。 适合人群:电力电子工程师、科研人员、高校学生等对逆变器仿真感兴趣的群体。 使用场景及目标:适用于研究和开发高效、稳定的逆变器系统,旨在通过仿真手段减少实验成本,优化设计方案,提高系统的性能指标。 其他说明:文中提供了详细的建模步骤和技术细节,帮助读者更好地理解和掌握相关技术和方法。同时,强调了仿真参数的选择和优化对于获得理想仿真结果的重要性。

    计算机红外通信.pdf

    计算机红外通信.pdf

    软考考试学习必备资料.md

    软考考试学习必备资料.md

    基于cornerstonejs开发移动端

    基于cornerstonejs开发移动端

    JavaScript网页设计高级案例:构建交互式图片画廊#JavaScript

    构建交互式图片画廊

    在学习Wpf的过程中,手搓了一个2048

    源码

    Bosch Rexroth IndraWorks Ds IndraWorks Ds 14V16.310.0

    Bosch Rexroth IndraWorks Ds IndraWorks Ds 14V16.310.0

    java面向对象 - 类与对象

    java面向对象 - 类与对象

    电机控制领域无感FOC算法的AT32平台实现及其鲁棒性优化

    内容概要:本文详细介绍了基于AT32平台的无感FOC(Field-Oriented Control)控制算法,特别是针对永磁同步电机(PMSM)和无刷直流电机(BLDC)的位置速度观测器实现。文章首先展示了启动策略的独特之处,即跳过传统前馈强拖阶段,直接利用矢量控制环和观测器协同启动。接着深入探讨了磁链观测器的核心算法,包括磁链积分、反正切求角度以及速度估算部分使用的改良版PLL。此外,文中还提到了容差配置模块,用于提高系统的鲁棒性和稳定性。最后,强调了模块间良好的解耦设计,使得各功能模块拥有明确的输入输出接口,增强了代码的可维护性和移植性。 适合人群:从事电机控制系统开发的技术人员,尤其是对无感FOC算法感兴趣的工程师。 使用场景及目标:适用于需要高精度、快速响应的电机控制系统开发项目,旨在提升系统的鲁棒性和稳定性,特别是在电机参数存在偏差的情况下依然能够保持良好性能。 其他说明:文章不仅提供了详细的代码实现,还分享了许多实用的经验和技术细节,如启动策略、磁链观测器的物理本质、速度估算方法等,有助于读者更好地理解和应用无感FOC算法。

    计算机机房de设置与维护.pdf

    计算机机房de设置与维护.pdf

    《Java 面试进阶指北 》 质量很高,专为面试打造

    《Java 面试进阶指北 》 质量很高,专为面试打造

    外转子开关磁阻电机多目标优化的NSGA-II算法实现与Matlab代码解析

    内容概要:本文详细介绍了外转子开关磁阻电机(ER-SRM)的多目标优化方法,主要采用NSGA-II算法进行优化。文章首先解释了为什么ER-SRM比传统内转子电机更难以优化,接着展示了如何利用NSGA-II算法解决这一难题。文中提供了详细的Matlab代码,包括种群初始化、交叉变异操作、非支配排序以及目标函数的定义。此外,还讨论了优化过程中的一些注意事项,如初始种群多样性的保持、交叉变异参数的选择、目标函数的设计等。最后,通过具体的案例和图表展示了优化结果及其应用价值。 适合人群:从事电机设计与优化的研究人员和技术人员,尤其是对外转子开关磁阻电机感兴趣的读者。 使用场景及目标:适用于需要同时优化电机效率、转矩波动和制造成本等多种目标的情况。通过NSGA-II算法,可以在多个相互冲突的目标间找到最佳平衡点,从而提高电机的整体性能。 其他说明:文章不仅提供了完整的Matlab代码实现,还分享了许多实践经验,如参数设置的经验公式、常见错误及解决方案等。这对于理解和掌握NSGA-II算法的实际应用非常有帮助。

    "慢行智远"是一款专业的串口数据采集与波形分析软件 软件支持多通道波形显示、数据记录、协议解析等功能,界面友好,操作简便,是您进行串口通信与数据分析的得力助手

    慢行智远V2.0"是一款专业的串口数据采集与信号分析软件,集成了多通道数据采集、实时波形显示、FFT频谱分析、FIR滤波处理等高级功能。软件提供直观的用户界面,支持亮色/暗色两种主题,具备强大的数据处理与可视化能力。核心功能包括: 全面的串口通信支持(多种波特率、数据位、停止位、校验位配置) 多通道(最多4通道)波形实时显示与分析 高级信号处理(FFT频谱分析、FIR滤波、信号平滑等) 智能数据管理(断行数据处理、大数据量优化) 数据记录与导出(文本、CSV、图像多种格式) 自适应界面设计(支持高DPI显示、暗色主题) 适用人群 嵌入式开发工程师:需要通过串口调试单片机、开发板等嵌入式设备 电子工程师:进行电路测试、信号采集与分析的专业人员 工业自动化技术人员:监测工业设备数据、进行状态分析 科研教育工作者:用于实验数据采集、科学研究与教学演示 医疗设备开发人员:分析生物电信号、开发医疗监测设备 物联网开发者:调试传感器网络、分析传感器数据 硬件测试工程师:进行产品质量检测、性能评估 使用场景及目标 研发调试场景 单片机开发:实时监控传感器数据、调试通信协议、观察系统运行状态等等

Global site tag (gtag.js) - Google Analytics