struts1 初始化流程说明

僤鯓⒐⒋嵵緔 提交于 2019-11-29 15:20:55

struts初始化起始于ActionServlet。正如其名,它是Servlet,按照Servlet的声明周期,struts的初始化放在了init方法之中。

public void init() throws ServletException {

    // struts初始化流程放入try/catch中,这样可以更好的处理未捕获的异常或错误
    try {
    	// 1. 初始化内部国际化信息
        initInternal();
        // 2. 判断convertNull,进行特殊类型转换器注册
        initOther();
        // 3. 获取当前serverlet的url-pattern
        initServlet();

        
        // 4. 将自身(ActionServlet)放入Servlet上下文中
        getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
        // 5. 判断用户是否有自定义模块初始化功能,若存在注册到模块工厂
        initModuleConfigFactory();

        // 6. 初始化模块默认配置文件
        ModuleConfig moduleConfig = initModuleConfig("", config);
        // 7. 初始化自定义国际化信息
        initModuleMessageResources(moduleConfig);
        // 8. 初始化数据源
        initModuleDataSources(moduleConfig);
        // 9. 初始化struts插件
        initModulePlugIns(moduleConfig);
        // 10. 冻结配置信息
        moduleConfig.freeze();

        // 11. 初始化struts自定义模块配置信息
        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();
        }

        // 12. 存储模块前缀,放入ServletContext
        this.initModulePrefixes(this.getServletContext());

        // 13. 销毁digester
        this.destroyConfigDigester();
    } catch (UnavailableException ex) {
        throw ex;
    } catch (Throwable t) {

        // The follow error message is not retrieved from internal message
        // resources as they may not have been able to have been 
        // initialized
        log.error("Unable to initialize Struts ActionServlet due to an "
            + "unexpected exception or error thrown, so marking the "
            + "servlet as unavailable.  Most likely, this is due to an "
            + "incorrect or missing library dependency.", t);
        throw new UnavailableException(t.getMessage());
    }    
}


1. initInternal()

使用struts*.jar中的org/apache/struts/action/ActionResources*.properties文件初始化国际化信息。

1.1 ActionResources*仅包含默认英文和日文两类,因此初始化过程中发生的异常信息仅能使用以上两种语言进行提示;

1.2 方法初始化一个MessageResources对象,初始化流程和里面的内容稍后介绍,目前仅需要知道国际化信息存放到了internal中;

1.3 若国际化信息初始化失败(目前仅可能是国际化文件未找到),抛出UnavailableException

protected MessageResources internal = null;

protected void initInternal() throws ServletException {

    try {
        internal = MessageResources.getMessageResources(internalName);
    } catch (MissingResourceException e) {
        log.error("Cannot load internal resources from '" + internalName + "'",
            e);
        throw new UnavailableException
            ("Cannot load internal resources from '" + internalName + "'");
    }

}


2. initOther()

struts的form bean使用beanUtil进行转换。initOther对null进行了指定处理,保证特殊类可以正确转换为form bean。

2.1 参数使用web.xml中actionServlet的参数convertNull;

2.2 struts为保证严格的兼容性,建议设置为true

protected void initOther() throws ServletException {

    String value = null;
    value = getServletConfig().getInitParameter("config");
    if (value != null) {
        config = value;
    }

    // Backwards compatibility for form beans of Java wrapper classes
    // Set to true for strict Struts 1.0 compatibility
    value = getServletConfig().getInitParameter("convertNull");
    if ("true".equalsIgnoreCase(value)
        || "yes".equalsIgnoreCase(value)
        || "on".equalsIgnoreCase(value)
        || "y".equalsIgnoreCase(value)
        || "1".equalsIgnoreCase(value)) {

        convertNull = true;
    }

    if (convertNull) {
        ConvertUtils.deregister();
        ConvertUtils.register(new BigDecimalConverter(null), BigDecimal.class);
        ConvertUtils.register(new BigIntegerConverter(null), BigInteger.class);
        ConvertUtils.register(new BooleanConverter(null), Boolean.class);
        ConvertUtils.register(new ByteConverter(null), Byte.class);
        ConvertUtils.register(new CharacterConverter(null), Character.class);
        ConvertUtils.register(new DoubleConverter(null), Double.class);
        ConvertUtils.register(new FloatConverter(null), Float.class);
        ConvertUtils.register(new IntegerConverter(null), Integer.class);
        ConvertUtils.register(new LongConverter(null), Long.class);
        ConvertUtils.register(new ShortConverter(null), Short.class);
    }

}


3. initServlet()

获取ActionServlet对应的url-pattern,即ActionServlet所能够拦截的请求类型,配置的值将存放在ServletContext中。


4. ActionServlet存储

将actionServlet存放到ServletContext中,需要注意的是ActionServlet是单例的。


5. initModuleConfigFactory()

struts的配置信息存放在ModuleConfig中(后文详细介绍),使用工厂模式进行创建ModuleConfigImpl对象,参数使用web.xml中ActionServlet的configFactory参数。这个配置不是必须的,因为Struts有默认的实现工厂类DefaultModuleConfigFactory。


6. initModuleConfig("", config)

initModuleConfig方法用于初始化struts配置文件,这块是struts初始化的核心内容。

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

    // :FIXME: Document UnavailableException? (Doesn't actually throw anything)

    if (log.isDebugEnabled()) {
        log.debug(
            "Initializing module path '"
                + prefix
                + "' configuration from '"
                + paths
                + "'");
    }

    /*
     *  获取工程类并创建ModuleConfig:
     *  1. createFactory常见工厂类,默认是DefaultModuleConfigFactory,若用户在第5步中自定义了工厂类,将创建该工厂类的实例;
     *  2. 使用抽象工厂模式,createModuleConfig是抽象方法,用于创建ModuleConfigImpl。DefaultModuleConfigFactory实现createModuleConfig方法并通过prefix区分并创建ModuleConfigImpl
     */
    ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
    ModuleConfig config = factoryObject.createModuleConfig(prefix);

    // 初始化解析器
    Digester digester = initConfigDigester();

    // 若配置多个struts配置文件,遍历解析
    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);
    }

    // 使用prefix进行区分,将对应的ModuleConfigImpl存放到ServletContext
    getServletContext().setAttribute(
        Globals.MODULE_KEY + config.getPrefix(),
        config);

    // 获取配置文件中的FormBean配置,如果是DynaActionForm进行初始化操作
    FormBeanConfig fbs[] = config.findFormBeanConfigs();
    for (int i = 0; i < fbs.length; i++) {
        if (fbs[i].getDynamic()) {
            fbs[i].getDynaActionFormClass();
        }
    }

    return config;
}


7. initModuleMessageResources(moduleConfig)

moduleConfig是struts-config.xml配置信息存放的对象,也就是第5步中所说的ModuleConfigImpl,里面存放有国际化信息:

7.1 国际化信息可以是多个,每个国际化对应一个抽象类MessageResources;

7.2 struts对MessageResources的默认实现是PropertyMessageResources;

7.3 国际化信息内容在首次获取时写入HashMap;

protected void initModuleMessageResources(ModuleConfig config)
    throws ServletException {

	// 获取struts-config.xml配置的国际化信息
    MessageResourcesConfig mrcs[] = config.findMessageResourcesConfigs();
    for (int i = 0; i < mrcs.length; i++) {
        if ((mrcs[i].getFactory() == null)
            || (mrcs[i].getParameter() == null)) {
            continue;
        }
        if (log.isDebugEnabled()) {
            log.debug(
                "Initializing module path '"
                    + config.getPrefix()
                    + "' message resources from '"
                    + mrcs[i].getParameter()
                    + "'");
        }

        // 若配置了国际化工厂类,使用自定义工厂类,否则使用默认的PropertyMessageResourcesFactory
        String factory = mrcs[i].getFactory();
        MessageResourcesFactory.setFactoryClass(factory);
        MessageResourcesFactory factoryObject =
            MessageResourcesFactory.createFactory();
        
        // 设置国际化配置信息
        factoryObject.setConfig(mrcs[i]);

        // 通过配置中的parameter参数创建国际化信息存储对象,parameter用于稳定国际化文件的配置
        MessageResources resources =
            factoryObject.createResources(mrcs[i].getParameter());
        
        // 参数null用于设置国际化信息未找到时的显示内容,true-null false-返回包含key值、国际化等信息的字符串
        resources.setReturnNull(mrcs[i].getNull());
        // 设置转义参数:对单引号'做特殊处理
        resources.setEscape(mrcs[i].isEscape());
        // 将国际化信息以配置信息的key参数和prefix作为 key值存放到ServletContext
        getServletContext().setAttribute(
            mrcs[i].getKey() + config.getPrefix(),
            resources);
    }

}


8. initModuleDataSources(moduleConfig)

初始化数据源,初始化之后的数据源根据配置文件的prefix(配置文件前缀),放置到ServletContext。

    protected void initModuleDataSources(ModuleConfig config) throws ServletException {

        // :FIXME: Document UnavailableException?

        if (log.isDebugEnabled()) {
            log.debug("Initializing module path '" + config.getPrefix() +
                "' data sources");
        }

        // PrintWriter的特殊实现类,将出现新行、flush()被调用、println被调用时将信息输出到ServletContext
        ServletContextWriter scw =
            new ServletContextWriter(getServletContext());
        
        // 读取数据源配置信息
        DataSourceConfig dscs[] = config.findDataSourceConfigs();
        if (dscs == null) {
            dscs = new DataSourceConfig[0];
        }

        // 使用FastHashMap存储数据源信息
        dataSources.setFast(false);
        for (int i = 0; i < dscs.length; i++) {
            if (log.isDebugEnabled()) {
                log.debug("Initializing module path '" + config.getPrefix() +
                    "' data source '" + dscs[i].getKey() + "'");
            }
            DataSource ds = null;
            try {
                ds = (DataSource)
                    RequestUtils.applicationInstance(dscs[i].getType());
                BeanUtils.populate(ds, dscs[i].getProperties());
                ds.setLogWriter(scw);

            } catch (Exception e) {
                log.error(internal.getMessage("dataSource.init", dscs[i].getKey()), e);
                throw new UnavailableException
                    (internal.getMessage("dataSource.init", dscs[i].getKey()));
            }
            
            // 使用数据源key参数和prefix做key,存放到ServletContext中
            getServletContext().setAttribute
                (dscs[i].getKey() + config.getPrefix(), ds);
            dataSources.put(dscs[i].getKey(), ds);
        }

        dataSources.setFast(true);

    }


9. initModulePlugIns(ModuleConfig config)

初始化struts插件,在1.2.9版本中,struts提供4类插件:DigestingPlugIn、ModuleConfigVerifier、TilesPlugin、ValidatorPlugIn。

初始化后的插件信息将通过prefix进行区分后存放到ServletContext。

    protected void initModulePlugIns
        (ModuleConfig config) throws ServletException {

        if (log.isDebugEnabled()) {
            log.debug("Initializing module path '" + config.getPrefix() + "' plug ins");
        }

        // 获取配置文件中的插件信息并创建插件对象
        PlugInConfig plugInConfigs[] = config.findPlugInConfigs();
        PlugIn plugIns[] = new PlugIn[plugInConfigs.length];

        // 使用prefix存放到ServletContext
        getServletContext().setAttribute(Globals.PLUG_INS_KEY + config.getPrefix(), plugIns);
        for (int i = 0; i < plugIns.length; i++) {
            try {
                plugIns[i] =
                    (PlugIn)RequestUtils.applicationInstance(plugInConfigs[i].getClassName());
                 BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties());
                  // Pass the current plugIn config object to the PlugIn.
                  // The property is set only if the plugin declares it.
                  // This plugin config object is needed by Tiles
                try {
                    PropertyUtils.setProperty(
                        plugIns[i],
                        "currentPlugInConfigObject",
                        plugInConfigs[i]);
                } catch (Exception e) {
                  // FIXME Whenever we fail silently, we must document a valid reason
                  // for doing so.  Why should we fail silently if a property can't be set on
                  // the plugin?
                    /**
                     * Between version 1.138-1.140 cedric made these changes.
                     * The exceptions are caught to deal with containers applying strict security.
                     * This was in response to bug #15736
                     *
                     * Recommend that we make the currentPlugInConfigObject part of the PlugIn Interface if we can, Rob
                     */
                }
                plugIns[i].init(this, config);

            } catch (ServletException e) {
                throw e;
            } catch (Exception e) {
                String errMsg =
                    internal.getMessage(
                        "plugIn.init",
                        plugInConfigs[i].getClassName());

                log(errMsg, e);
                throw new UnavailableException(errMsg);
            }
        }

    }


10. moduleConfig.freeze()

将ModuleConfigImpl的configured属性设置为true。其他配置信息进行变更前都会判断configured属性,而ModuleConfigImpl没有提供将configured设置为false的方法,因为配置文件一旦冻结,不可修改。


11. 初始化struts自定义模块配置信息

根据web.xml中ActionServlet启动参数进行其他配置文件的初始化工作:

11.1 配置信息需要以config开头;

11.2 每一个配置文件对应一个MuduleConfigImpl,用过前缀进行区分,存放到ServletContext;

11.3 每个配置文件的信息都是独立的,作用于仅限当前配置文件;

11.4 每个配置文件在进行初始化操作后都会冻结;


12. this.initModulePrefixes(this.getServletContext());

将所有配置文件的前缀放入ServletContext;


13. this.destroyConfigDigester();

销毁解析器。


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!