现在的位置: 首页 > 综合 > 正文

Struts的资源文件时如何初始化的–struts源码学习

2013年04月09日 ⁄ 综合 ⁄ 共 13066字 ⁄ 字号 评论关闭
分以下几步来理解:
[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);
 
//

抱歉!评论已关闭.