zhyiwww
用平实的笔,记录编程路上的点点滴滴………
posts - 536,comments - 394,trackbacks - 0

Struts 的资源文件时如何初始化的

|-----------------------------------------------------------------------------------------------------|

|                                 作者:zhyiwww        zhyiwww@163.com                          |

|                                                   转载请注明出处                                           |
|-----------------------------------------------------------------------------------------------------|

分以下几步来理解:

[1] Web 应用如何知道使用的是 Struts

我们知道,在 Web 工程的 /WEB-INF/ 目录下面,我们有两个配置文件

Web.xml 和 Struts-config.xml.

一般情况下就只有一个 web.xml 文件,如果系统是用了 struts 技术的话,那么就会有 struts-config.xml 文件。

[2] 系统是如何启动和初始化 struts 的

在 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 >

    < init-param >

      < param-name > debug </ param-name >

      < param-value > 3 </ param-value >

    </ init-param >

    < init-param >

      < param-name > detail </ param-name >

      < param-value > 3 </ param-value >

    </ init-param >

    < load-on-startup > 0 </ load-on-startup >

  </ servlet >

  < servlet >

    < servlet-name > startThread </ servlet-name >

    < servlet-class > com.cgogo.ypindex.StartThread </ servlet-class >

    < init-param >

    < param-name > startParam </ param-name >

    < param-value > 2 </ param-value >

    </ init-param >

    < load-on-startup > 1 </ load-on-startup >

  </ servlet >  

  < servlet-mapping >

    < servlet-name > action </ servlet-name >

    < url-pattern > *.do </ url-pattern >

  </ servlet-mapping >

在 web.xml 中会配置 struts 的中央控制器类。 Web.xml 配置文件的初始化是由容器来实现载入和初始化的。如果你使用的是 tomcat 的话,那么就是由 tomcat 在启动的时候会载入此 web.xml 文件,也就为此 web 应用创建了一个 web Context, 此 context 也就是你 Web 应用的访问入口。

载入中央控制器 org.apache.struts.action.ActionServlet ,这是一个 Servlet, 那么,其就要进行 servlet 的初始化操作。此 action 是如何对 Struts 系统进行初始化的呢?

[3] init()-- ActionServlet 如何初始化 Struts 系统?

看一下 ActionServlet 的源代码:

我们知道,在一个 Servlet 中,在其启动的时候,首先要执行 init() 方法,那么我们先来看一下 actionServlet 的 init() 方法的源代码:

    /**

     * <p>Initialize this servlet.  Most of the processing has been factored into

     * support methods so that you can override particular functionality at a

     * fairly granular level.</p>

     *

     * @exception ServletException if we cannot configure ourselves correctly

     */

    public void init() throws ServletException {

 

        // Wraps the entire initialization in a try/catch to better handle

        // unexpected exceptions and errors to provide better feedback

        // to the developer

        try {

(1)      initInternal();

(2)      initOther();

(3)      initServlet();

   

            getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);

(4)      initModuleConfigFactory();

            // Initialize modules as needed

            ModuleConfig moduleConfig = initModuleConfig("", config);

(5)      initModuleMessageResources(moduleConfig);

(6)      initModuleDataSources(moduleConfig);

(7)      initModulePlugIns(moduleConfig);

            moduleConfig.freeze();

(8)      初始化配置参数

            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();

        } 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() 。

 

[4]   initInternal()-- 内部资源文件的初始化

initInternal() 就是实现内部资源文件的初始化的,也就是转为 Struts 系统本身提供的以下错误信息提示等文字的国际化实现的。我们看一下源代码是如何实现的:

    /**

     * <p> Initialize our internal MessageResources bundle. </p>

     *

     * @exception ServletException if we cannot initialize these resources

     */

    protected void initInternal() throws ServletException {

 

        // : FIXME : Document UnavailableException

 

        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 + "'" );

        }

 

}

Struts 根据配置文件的名字得到一个资源文件。

internalName 就是内部资源文件的名称,其在 actionServlet 中定义:

    /**

     * <p> The Java base name of our internal resources. </p>

     * @since Struts 1.1

     */

    protected String internalName = "org.apache.struts.action.ActionResources" ;

取得后的对象是一个 MessageResources 的对象,保存在 internal 中,

    /**

     * <p>The resources object for our internal resources.</p>

     */

    protected MessageResources internal = null;

经过此初始化后, internal 就不在是 null 了。

至此就实现完成了内部资源文件的初始化。如果出现了异常的话,那么系统就捕捉到。

 

然后,系统就开始初始化其它的配置,即( 2 ) initOther();

 

[5] initOther()-- 如何初始化其他的配置的?

    /**

     * <p>Initialize other global characteristics of the controller servlet.</p>

     *

     * @exception ServletException if we cannot initialize these resources

     */

    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();

 

[6]   initServlet()-- 如何初始化 servlet

这个初始化主要是初始化 servlet 的,哪些 servlet 呢?就是我们在 web.xml 中配置的那些需要在 web application 初始化时就栽入系统的 servlet

这是一个复杂的过程:

 

我的理解:

       这部分代码就执行一次,仅在初始化的时候执行一次。

           /**

     * <p>Initialize the servlet mapping under which our controller servlet

     * is being accessed.  This will be used in the <code>&html:form&gt;</code>

     * tag to generate correct destination URLs for form submissions.</p>

     *

     * @throws ServletException if error happens while scanning web.xml

     */

    protected void initServlet() throws ServletException {

 

        // Remember our servlet name

       // 这里保存当前的servlet名字,保存在actionServlet的servletName属性中

        this.servletName = getServletConfig().getServletName();

 

        // Prepare a Digester to scan the web application deployment descriptor

             

        Digester digester = new Digester();

        digester.push(this);

        digester.setNamespaceAware(true);

        digester.setValidating(false);

 

        // Register our local copy of the DTDs that we can find

        for (int i = 0; i < registrations.length; i += 2) {

            URL url = this.getClass().getResource(registrations[i+1]);

            if (url != null) {

                digester.register(registrations[i], url.toString());

            }

        }

 

        // Configure the processing rules that we need

        digester.addCallMethod("web-app/servlet-mapping",

                               "addServletMapping", 2);

        digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);

        digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);

 

        // Process the web application deployment descriptor

        if (log.isDebugEnabled()) {

            log.debug("Scanning web.xml for controller servlet mapping");

        }

 

    // 取得当前的配置文件   

InputStream input =

            getServletContext().getResourceAsStream("/WEB-INF/web.xml");

 

        if (input == null) {

            log.error(internal.getMessage("configWebXml"));

            throw new ServletException(internal.getMessage("configWebXml"));

        }

 

        try {

            // 解析当前配置文件

digester.parse(input);

 

        } catch (IOException e) {

            log.error(internal.getMessage("configWebXml"), e);

            throw new ServletException(e);

 

        } catch (SAXException e) {

            log.error(internal.getMessage("configWebXml"), e);

            throw new ServletException(e);

 

        } finally {

            try {

                // 解析完毕,关闭输入

input.close();

            } catch (IOException e) {

                log.error(internal.getMessage("configWebXml"), e);

    /**           

如果有异常,当前部进行处理,而是留给他的调用者来处理。其实是当前的调用部分没有处理的能力。我们可以这样理解,假设你想在出现了这类异常异常的地方给用户一个提示,但是在我们封装功能实现的时候,我们并不知道谁会来调用,所以我们只有把异常抛出,让调用者自己去处理。

    这一点也许不太好理解,不过,如果理解了,可能你就能够灵活的使用异常了。

*/

throw new ServletException(e);

            }

        }

 

        // Record a servlet context attribute (if appropriate)

        if (log.isDebugEnabled()) {

            log.debug("Mapping for servlet '" + servletName + "' = '" +

                servletMapping + "'");

        }

 

        if (servletMapping != null) {

            getServletContext().setAttribute(Globals.SERVLET_KEY, servletMapping);

        }

 

    }

[7] 初始化其他模块

(1)   初始化工厂

            getServletContext().setAttribute( Globals . ACTION_SERVLET_KEY , this );

            initModuleConfigFactory();

(2)   初始化资源模块

            // Initialize modules as needed

            ModuleConfig moduleConfig = initModuleConfig( "" , config );

             initModuleMessageResources(moduleConfig);

(3)   初始化数据源配置模块

            initModuleDataSources(moduleConfig);

(4)   初始化 PlugIn 模块

            initModulePlugIns(moduleConfig);

            moduleConfig.freeze();

[8]   初始化参数

            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();

            }

[9]   我也不知道做什么用的    

            this .initModulePrefixes( this .getServletContext());

   

            this .destroyConfigDigester();

以上就是Struts的初始化流程。

[10]          部分模块的详细实现:

a.       工厂的初始化如何实现的:

    /**

     * <p>Initialize the factory used to create the module configuration.</p>

     * @since Struts 1.2

     */

protected void initModuleConfigFactory(){

/**

这个部分的代码就是取得参数。

这个参数你可以自己扩展你的模块实现工厂。但是一般都没有自己去做。

所以一般都使用的默认的工厂初始化配置。

*/

        String configFactory = getServletConfig().getInitParameter("configFactory");

/**

下面的代码,只有你做了自己的配置才会有效。否则一般是不执行的。

*/

        if (configFactory != null) {

/**

设置此工厂,并把其参数存入到 ModuleConfigFactory.factoryClass 属性中。

此部分可以看 ModuleConfigFactory 的代码。 ModuleConfigFactory 是一个

*/

            ModuleConfigFactory.setFactoryClass(configFactory);

        }

    }

b.       资源模块式如何初始化的

// 调用的部分

protected String config = "/WEB-INF/struts-config.xml";

------------------------------------------------------------------          

ModuleConfig moduleConfig = initModuleConfig("", config);

initModuleMessageResources(moduleConfig);

       // 实现的部分:

    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()

                        + "'");

            }

/**

           protected String factory =

        "org.apache.struts.util.PropertyMessageResourcesFactory";

就是返回的这个值,如果你没有做其他的设置的话。

一般情况下,我们都用得是默认的

*/

            String factory = mrcs[i].getFactory();

/**

       此处对每一个资源配置文件都回去创建一个工厂

*/

            MessageResourcesFactory.setFactoryClass(factory);

            MessageResourcesFactory factoryObject =

                MessageResourcesFactory.createFactory();

            factoryObject.setConfig(mrcs[i]);

            MessageResources resources =

                factoryObject.createResources(mrcs[i].getParameter());

            resources.setReturnNull(mrcs[i].getNull());

            resources.setEscape(mrcs[i].isEscape());

 

       /**

这一部分非常重要。

我们之所以能够直接调用,就是因为,初始化后,我们就把此 resources 放到了其对应的当前应用的属性值里面了。之后我们就可以直接调用了。
       */

            getServletContext().setAttribute(

                mrcs[i].getKey() + config.getPrefix(),

                resources);

        }

 

    }

c.       数据模块是如何初始化的

==========================================================

// 调用部分

initModuleDataSources(moduleConfig);

 

// 实现部分:

/**

     * <p>Initialize the data sources for the specified module.</p>

     *

     * @param config ModuleConfig information for this module

     *

     * @exception ServletException if initialization cannot be performed

     * @since Struts 1.1

     */

    protected void initModuleDataSources(ModuleConfig config) throws ServletException {

 

        // :FIXME: Document UnavailableException?

 

        if (log.isDebugEnabled()) {

            log.debug("Initializing module path '" + config.getPrefix() +

                "' data sources");

        }

 

        ServletContextWriter scw =

            new ServletContextWriter(getServletContext());

/**

因为你可能配置了多个数据源,所以此处返回的是一个数组
*/

        DataSourceConfig dscs[] = config.findDataSourceConfigs();

    // 处理没有配置数据源的情况

if (dscs == null) {

dscs = new DataSourceConfig[0];

        }

/**

           /**

     * <p>The JDBC data sources that has been configured for this module,

     * if any, keyed by the servlet context attribute under which they are

     * stored.</p>

     */

protected FastHashMap dataSources = new FastHashMap();

这是一个加工过的 HashMap ,又不同的工作模式

 

*/

 

        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()));

            }

/**

       这一个部分很重要。

       把初始化后的数据源放入到 servlet 的属性中,所以我们才可以通过 struts 的对应属性直接访问。

  protected String key = Globals.DATA_SOURCE_KEY;

所以,我们可以通过 Globals.DATA_SOURCE_KEY 属性的值来取得其配制后的数据源。

如果多个的话,可以通过数据源的参数 ID 来配置和调用。

*/

            getServletContext().setAttribute

                (dscs[i].getKey() + config.getPrefix(), ds);

            dataSources.put(dscs[i].getKey(), ds);

        }

 

        dataSources.setFast(true);

 

    }

 

至此,Struts系统初始化完毕。

 

 

 

 

 



|----------------------------------------------------------------------------------------|
                           版权声明  版权所有 @zhyiwww
            引用请注明来源 http://www.blogjava.net/zhyiwww   
|----------------------------------------------------------------------------------------|
posted on 2006-12-25 16:47 zhyiwww 阅读(3429) 评论(1)  编辑  收藏 所属分类: j2ee

FeedBack:
# re: Struts的资源文件时如何初始化的--struts源码学习
2007-01-25 13:49 | bearshy
顶  回复  更多评论