分析 Struts中的struts-config.xml解析過程

/**
     * <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>
     *
     * @throws ServletException if we cannot configure ourselves correctly
     */
    public void init() throws ServletException {
        final String configPrefix = "config/";
        final int configPrefixLength = configPrefix.length() - 1;

        // Wraps the entire initialization in a try/catch to better handle
        // unexpected exceptions and errors to provide better feedback
        // to the developer
        try {
            initInternal();
            initOther();
            initServlet();
            initChain();

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

            // Initialize modules as needed
            ModuleConfig moduleConfig = initModuleConfig("", config);

            initModuleMessageResources(moduleConfig);
            initModulePlugIns(moduleConfig);
            initModuleFormBeans(moduleConfig);
            initModuleForwards(moduleConfig);
            initModuleExceptionConfigs(moduleConfig);
            initModuleActions(moduleConfig);
            moduleConfig.freeze();

            Enumeration names = getServletConfig().getInitParameterNames();

            while (names.hasMoreElements()) {
                String name = (String) names.nextElement();

                if (!name.startsWith(configPrefix)) {
                    continue;
                }

                String prefix = name.substring(configPrefixLength);

                moduleConfig =
                    initModuleConfig(prefix,
                        getServletConfig().getInitParameter(name));
                initModuleMessageResources(moduleConfig);
                initModulePlugIns(moduleConfig);
                initModuleFormBeans(moduleConfig);
                initModuleForwards(moduleConfig);
                initModuleExceptionConfigs(moduleConfig);
                initModuleActions(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());
        }
    }

ModuleConfig moduleConfig = initModuleConfig("", config) ,在這一節中我們將要仔細分析這個方法的源代碼,

/**
     * <p>Initialize the module configuration information for the specified
     * module.</p>
     *
     * @param prefix Module prefix for this module
     * @param paths  Comma-separated list of context-relative resource path(s)
     *               for this modules's configuration resource(s)
     * @return The new module configuration instance.
     * @throws ServletException if initialization cannot be performed
     * @since Struts 1.1
     */
    protected ModuleConfig initModuleConfig(String prefix, String paths)
        throws ServletException {
        if (log.isDebugEnabled()) {
            log.debug("Initializing module path '" + prefix
                + "' configuration from '" + paths + "'");
        }

        // Parse the configuration for this module
        ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();

        /**  

          *通過prefix的值來創建ModuleConfig對象

          *這裏的prefix的含意是,模塊名稱,在struts中,如果配置多個配置文件,是要在.web.xml中指定的路徑是 config/name/***.xml 這裏的name就是prefix了

          *<init-param>

          *     <param-name>config/name</param-name>  

          *    <param-value>/WEB-INF/struts-config.xml</param-value>  

          *</init-param>

          * 如這個web.xml中的配置中的, name/即爲模塊的prefix  

          */
        ModuleConfig config = factoryObject.createModuleConfig(prefix);

        // Configure the Digester instance we will use

       // 初使化並,得到Digester對象(在初使化的過程中一定會添加各種監聽,我們一會將看一下初使化的具體代碼)
        Digester digester = initConfigDigester();

        List urls = splitAndResolvePaths(paths);
        URL url;

        for (Iterator i = urls.iterator(); i.hasNext();) {
            url = (URL) i.next();
            digester.push(config);
            this.parseModuleConfigFile(digester, url);
        }

        getServletContext().setAttribute(Globals.MODULE_KEY
            + config.getPrefix(), config);

        return config;
    }

整個方法中很明確,首先,通過,initConfigDigester();方法來得到一個,已經被設置了各種監聽的Digester對象,然後將path路徑下的xml 文件進行解析.那麼,到底設置了哪些監聽規則呢?我們來跟到initConfigDigester()方法中看一下.

/**
     * <p>Create (if needed) and return a new <code>Digester</code> instance
     * that has been initialized to process Struts module configuration files
     * and configure a corresponding <code>ModuleConfig</code> object (which
     * must be pushed on to the evaluation stack before parsing begins).</p>
     *
     * @return A new configured <code>Digester</code> instance.
     * @throws ServletException if a Digester cannot be configured
     * @since Struts 1.1
     */
    protected Digester initConfigDigester()
        throws ServletException {
        // :FIXME: Where can ServletException be thrown?
        // Do we have an existing instance?

        //是否已經存在解析好了的configDigester對象了,如果是直接返回.       

         if (configDigester != null) {
            return (configDigester);
        }

        // Create a new Digester instance with standard capabilities

       //創建一個新的Digester對象,並設置參數
        configDigester = new Digester();

        //名字是否空間敏感.
        configDigester.setNamespaceAware(true);

        //設置是否驗證XML文檔有效
        configDigester.setValidating(this.isValidating());

         //設置是否使用上下文的ClassLoader(通過,Thread.currentThread().getContextClassLoader()得到的)來加載 

        //爲digester對象設置的監聽類   

        configDigester.setUseContextClassLoader(true);

        //爲digester對象來填加監聽類集合   
        configDigester.addRuleSet(new ConfigRuleSet());

        for (int i = 0; i < registrations.length; i += 2) {
            URL url = this.getClass().getResource(registrations[i + 1]);

            if (url != null) {
                configDigester.register(registrations[i], url.toString());
            }
        }

        this.addRuleSets();

        // Return the completely configured Digester instance
        return (configDigester);
    }

從這個方法中,我們可以看到,具體的匹配規則是通過,一個叫ConfigRuleSet()的對象傳進來的.

/**
 * <p>The set of Digester rules required to parse a Struts configuration file
 * (<code>struts-config.xml</code>).</p>
 *
 * @version $Rev: 471754 $ $Date: 2005-08-16 15:53:27 -0400 (Tue, 16 Aug 2005)
 *          $
 * @since Struts 1.1
 */
public class ConfigRuleSet extends RuleSetBase {
    // --------------------------------------------------------- Public Methods

    /**
     * <p>Add the set of Rule instances defined in this RuleSet to the
     * specified <code>Digester</code> instance, associating them with our
     * namespace URI (if any).  This method should only be called by a
     * Digester instance.  These rules assume that an instance of
     * <code>org.apache.struts.config.ModuleConfig</code> is pushed onto the
     * evaluation stack before parsing begins.</p>
     *
     * @param digester Digester instance to which the new Rule instances
     *                 should be added.
     */
    public void addRuleInstances(Digester digester) {
        ClassLoader cl = digester.getClassLoader();

        digester.addRule("struts-config/action-mappings",
            new SetActionMappingClassRule());

        digester.addFactoryCreate("struts-config/action-mappings/action",
            new ActionMappingFactory(cl));
        digester.addSetProperties("struts-config/action-mappings/action");
        digester.addSetNext("struts-config/action-mappings/action",
            "addActionConfig", "org.apache.struts.config.ActionConfig");

        digester.addRule("struts-config/action-mappings/action/set-property",
            new BaseConfigSetPropertyRule());

        digester.addObjectCreate("struts-config/action-mappings/action/exception",
            "org.apache.struts.config.ExceptionConfig", "className");
        digester.addSetProperties(
            "struts-config/action-mappings/action/exception");
        digester.addSetNext("struts-config/action-mappings/action/exception",
            "addExceptionConfig", "org.apache.struts.config.ExceptionConfig");

        digester.addRule("struts-config/action-mappings/action/exception/set-property",
            new BaseConfigSetPropertyRule());

        digester.addFactoryCreate("struts-config/action-mappings/action/forward",
            new ActionForwardFactory(cl));
        digester.addSetProperties(
            "struts-config/action-mappings/action/forward");
        digester.addSetNext("struts-config/action-mappings/action/forward",
            "addForwardConfig", "org.apache.struts.config.ForwardConfig");

        digester.addRule("struts-config/action-mappings/action/forward/set-property",
            new BaseConfigSetPropertyRule());

        digester.addObjectCreate("struts-config/controller",
            "org.apache.struts.config.ControllerConfig", "className");
        digester.addSetProperties("struts-config/controller");
        digester.addSetNext("struts-config/controller", "setControllerConfig",
            "org.apache.struts.config.ControllerConfig");

        digester.addRule("struts-config/controller/set-property",
            new BaseConfigSetPropertyRule());

        digester.addRule("struts-config/form-beans",
            new SetActionFormBeanClassRule());

        digester.addFactoryCreate("struts-config/form-beans/form-bean",
            new ActionFormBeanFactory(cl));
        digester.addSetProperties("struts-config/form-beans/form-bean");
        digester.addSetNext("struts-config/form-beans/form-bean",
            "addFormBeanConfig", "org.apache.struts.config.FormBeanConfig");

        digester.addObjectCreate("struts-config/form-beans/form-bean/form-property",
            "org.apache.struts.config.FormPropertyConfig", "className");
        digester.addSetProperties(
            "struts-config/form-beans/form-bean/form-property");
        digester.addSetNext("struts-config/form-beans/form-bean/form-property",
            "addFormPropertyConfig",
            "org.apache.struts.config.FormPropertyConfig");

        digester.addRule("struts-config/form-beans/form-bean/form-property/set-property",
            new BaseConfigSetPropertyRule());

        digester.addRule("struts-config/form-beans/form-bean/set-property",
            new BaseConfigSetPropertyRule());

        digester.addObjectCreate("struts-config/global-exceptions/exception",
            "org.apache.struts.config.ExceptionConfig", "className");
        digester.addSetProperties("struts-config/global-exceptions/exception");
        digester.addSetNext("struts-config/global-exceptions/exception",
            "addExceptionConfig", "org.apache.struts.config.ExceptionConfig");

        digester.addRule("struts-config/global-exceptions/exception/set-property",
            new BaseConfigSetPropertyRule());

        digester.addRule("struts-config/global-forwards",
            new SetActionForwardClassRule());

        digester.addFactoryCreate("struts-config/global-forwards/forward",
            new GlobalForwardFactory(cl));
        digester.addSetProperties("struts-config/global-forwards/forward");
        digester.addSetNext("struts-config/global-forwards/forward",
            "addForwardConfig", "org.apache.struts.config.ForwardConfig");

        digester.addRule("struts-config/global-forwards/forward/set-property",
            new BaseConfigSetPropertyRule());

        digester.addObjectCreate("struts-config/message-resources",
            "org.apache.struts.config.MessageResourcesConfig", "className");
        digester.addSetProperties("struts-config/message-resources");
        digester.addSetNext("struts-config/message-resources",
            "addMessageResourcesConfig",
            "org.apache.struts.config.MessageResourcesConfig");

        digester.addRule("struts-config/message-resources/set-property",
            new BaseConfigSetPropertyRule());

        digester.addObjectCreate("struts-config/plug-in",
            "org.apache.struts.config.PlugInConfig");
        digester.addSetProperties("struts-config/plug-in");
        digester.addSetNext("struts-config/plug-in", "addPlugInConfig",
            "org.apache.struts.config.PlugInConfig");

        digester.addRule("struts-config/plug-in/set-property",
            new PlugInSetPropertyRule());

        // PluginConfig does not extend BaseConfig, at least for now.
    }
}


/**
 * <p> Class that records the name and value of a configuration property to be
 * used in configuring a <code>PlugIn</code> instance when instantiated. </p>
 */
final class PlugInSetPropertyRule extends Rule {
    public PlugInSetPropertyRule() {
        super();
    }

    public void begin(String namespace, String names, Attributes attributes)
        throws Exception {
        PlugInConfig plugInConfig = (PlugInConfig) digester.peek();

        plugInConfig.addProperty(attributes.getValue("property"),
            attributes.getValue("value"));
    }
}


/**
 * <p> Class that sets the name of the class to use when creating action form
 * bean instances. The value is set on the object on the top of the stack,
 * which must be a <code>org.apache.struts.config.ModuleConfig</code>. </p>
 */
final class SetActionFormBeanClassRule extends Rule {
    public SetActionFormBeanClassRule() {
        super();
    }

    public void begin(String namespace, String name, Attributes attributes)
        throws Exception {
        String className = attributes.getValue("type");

        if (className != null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();

            mc.setActionFormBeanClass(className);
        }
    }
}


/**
 * <p> A variant of the standard Digester <code>SetPropertyRule</code>.  If
 * the element being processed has a "key" attribute, then the value will be
 * used to call <code>setProperty(key,value)</code> on the object on top of
 * the stack, which will be assumed to be of type <code>ActionConfig</code>.
 * Otherwise, the standard <code>SetPropertyRule</code> behavior is invoked,
 * and the value will be used to set a bean property on the object on top of
 * the Digester stack. In that case, the element being processed is assumed to
 * have attributes "property" and "value". </p>
 */
final class BaseConfigSetPropertyRule extends SetPropertyRule {
    public BaseConfigSetPropertyRule() {
        super("property", "value");
    }

    public void begin(Attributes attributes)
        throws Exception {
        if (attributes.getIndex("key") == -1) {
            super.begin(attributes);

            return;
        }

        if (attributes.getIndex("property") != -1) {
            throw new IllegalArgumentException(
                "<set-property> accepts only one of 'key' or 'property' attributes.");
        }

        Object topOfStack = digester.peek();

        if (topOfStack instanceof BaseConfig) {
            BaseConfig config = (BaseConfig) topOfStack;

            config.setProperty(attributes.getValue("key"),
                attributes.getValue("value"));
        } else {
            throw new IllegalArgumentException(
                "'key' attribute of <set-property> only applicable to subclasses of BaseConfig; "
                + "object on top of stack is " + topOfStack + " [key: "
                + attributes.getValue("key") + ", value: "
                + attributes.getValue("value") + "]");
        }
    }
}


/**
 * <p> An object creation factory which creates action form bean instances,
 * taking into account the default class name, which may have been specified
 * on the parent element and which is made available through the object on the
 * top of the stack, which must be a <code>org.apache.struts.config.ModuleConfig</code>.
 * </p>
 */
final class ActionFormBeanFactory extends AbstractObjectCreationFactory {
    private ClassLoader cl;

    public ActionFormBeanFactory(ClassLoader cl) {
        super();
        this.cl = cl;
    }

    public Object createObject(Attributes attributes) {
        // Identify the name of the class to instantiate
        String className = attributes.getValue("className");

        if (className == null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();

            className = mc.getActionFormBeanClass();
        }

        // Instantiate the new object and return it
        Object actionFormBean = null;

        try {
            actionFormBean = RequestUtils.applicationInstance(className, cl);
        } catch (Exception e) {
            digester.getLogger().error("ActionFormBeanFactory.createObject: ", e);
        }

        return actionFormBean;
    }
}


/**
 * <p> Class that sets the name of the class to use when creating action
 * mapping instances. The value is set on the object on the top of the stack,
 * which must be a <code>org.apache.struts.config.ModuleConfig</code>. </p>
 */
final class SetActionMappingClassRule extends Rule {
    public SetActionMappingClassRule() {
        super();
    }

    public void begin(String namespace, String name, Attributes attributes)
        throws Exception {
        String className = attributes.getValue("type");

        if (className != null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();

            mc.setActionMappingClass(className);
        }
    }
}


/**
 * <p> An object creation factory which creates action mapping instances,
 * taking into account the default class name, which may have been specified
 * on the parent element and which is made available through the object on the
 * top of the stack, which must be a <code>org.apache.struts.config.ModuleConfig</code>.
 * </p>
 */
final class ActionMappingFactory extends AbstractObjectCreationFactory {
    private ClassLoader cl;

    public ActionMappingFactory(ClassLoader cl) {
        super();
        this.cl = cl;
    }

    public Object createObject(Attributes attributes) {
        // Identify the name of the class to instantiate
        String className = attributes.getValue("className");

        if (className == null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();

            className = mc.getActionMappingClass();
        }

        // Instantiate the new object and return it
        Object actionMapping = null;

        try {
            actionMapping = RequestUtils.applicationInstance(className, cl);
        } catch (Exception e) {
            digester.getLogger().error("ActionMappingFactory.createObject: ", e);
        }

        return actionMapping;
    }
}


/**
 * <p> Class that sets the name of the class to use when creating global
 * forward instances. The value is set on the object on the top of the stack,
 * which must be a <code>org.apache.struts.config.ModuleConfig</code>. </p>
 */
final class SetActionForwardClassRule extends Rule {
    public SetActionForwardClassRule() {
        super();
    }

    public void begin(String namespace, String name, Attributes attributes)
        throws Exception {
        String className = attributes.getValue("type");

        if (className != null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();

            mc.setActionForwardClass(className);
        }
    }
}


/**
 * <p> An object creation factory which creates global forward instances,
 * taking into account the default class name, which may have been specified
 * on the parent element and which is made available through the object on the
 * top of the stack, which must be a <code>org.apache.struts.config.ModuleConfig</code>.
 * </p>
 */
final class GlobalForwardFactory extends AbstractObjectCreationFactory {
    private ClassLoader cl;

    public GlobalForwardFactory(ClassLoader cl) {
        super();
        this.cl = cl;
    }

    public Object createObject(Attributes attributes) {
        // Identify the name of the class to instantiate
        String className = attributes.getValue("className");

        if (className == null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();

            className = mc.getActionForwardClass();
        }

        // Instantiate the new object and return it
        Object globalForward = null;

        try {
            globalForward = RequestUtils.applicationInstance(className, cl);
        } catch (Exception e) {
            digester.getLogger().error("GlobalForwardFactory.createObject: ", e);
        }

        return globalForward;
    }
}


/**
 * <p> An object creation factory which creates action forward instances,
 * taking into account the default class name, which may have been specified
 * on the parent element and which is made available through the object on the
 * top of the stack, which must be a <code>org.apache.struts.config.ModuleConfig</code>.
 * </p>
 */
final class ActionForwardFactory extends AbstractObjectCreationFactory {
    private ClassLoader cl;

    public ActionForwardFactory(ClassLoader cl) {
        super();
        this.cl = cl;
    }

    public Object createObject(Attributes attributes) {
        // Identify the name of the class to instantiate
        String className = attributes.getValue("className");

        if (className == null) {
            ModuleConfig mc = (ModuleConfig) digester.peek(1);

            className = mc.getActionForwardClass();
        }

        // Instantiate the new object and return it
        Object actionForward = null;

        try {
            actionForward = RequestUtils.applicationInstance(className, cl);
        } catch (Exception e) {
            digester.getLogger().error("ActionForwardFactory.createObject: ", e);
        }

        return actionForward;
    }
}

下面我們就按照代碼中註釋的五個步驟一步一步分析

步驟一.  digester.addRule
            ("struts-config/action-mappings",
             new SetActionMappingClassRule());
當解析的時候碰到<action-mappings>標籤時,調用下面的方法

Java代碼 複製代碼
  1. public void begin(String namespace, String name, Attributes attributes) throws Exception {   
  2.         String className = attributes.getValue("type");   
  3.         if (className != null) {   
  4.             ModuleConfig mc = (ModuleConfig) digester.peek();   
  5.             mc.setActionMappingClass(className);   
  6.         }   
  7.     }  

我們看到它將取得<action-mappings >標籤中的type元素的值並把它設置給配置文件的Root元素ModuleConfig的actionMappingClass,這個域的值默認爲
org.apache.struts.action.ActionMapping

步驟二,   digester.addFactoryCreate
            ("struts-config/action-mappings/action",
             new ActionMappingFactory());
當遇到<action>標籤時,將使用ActionMappingFactory類的createObject方法來創建一個對象.

Java代碼 複製代碼
  1. public Object createObject(Attributes attributes) {   
  2.   
  3.        // Identify the name of the class to instantiate   
  4.       String className = attributes.getValue("className");   
  5.        if (className == null) {   
  6.            ModuleConfig mc = (ModuleConfig) digester.peek();   
  7.            className = mc.getActionMappingClass();   
  8.        }   
  9.   
  10.        // Instantiate the new object and return it   
  11.        Object actionMapping = null;   
  12.        try {   
  13.            actionMapping =   
  14.                RequestUtils.applicationInstance(className);   
  15.        } catch (Exception e) {   
  16.            digester.getLogger().error(   
  17.                    "ActionMappingFactory.createObject: ", e);   
  18.        }   
  19.   
  20.        return actionMapping;   
  21.    }  

通過,<action > 標籤中的className屬性來創建對象,當這個className沒有指定時,使用默認的
org.apache.struts.action.ActionMapping類來創建對象.

步驟三.    digester.addSetProperties
            ("struts-config/action-mappings/action");
當碰到<action>標籤時,使用<action >標籤中的屬性來設置  ActionMapping對象.
主要包括path, type,scope,validate等 這樣我們就可以看到,所有的關於bean的配置信息實際都是放在了
ActionMapping對象中了.
步驟四. digester.addSetNext
            ("struts-config/action-mappings/action",
             "addActionConfig",
             "org.apache.struts.config.ActionConfig");
我們知道,整個xml文件的解析的項層對象是一個ModuleConfig對象,那麼上面的這個方法的意思就是說,
當碰到<action>標籤的時候,我們把生成 的ActionMapping對象,通過調用ModuleConfig對象的addActionConfig方法設置給root對象.

  1. public void addActionConfig(ActionConfig config) {   
  2.   
  3.       if (configured) {   
  4.           throw new IllegalStateException("Configuration is frozen");   
  5.       }   
  6.       config.setModuleConfig(this);   
  7.   
  8.     //通過ActionMapping中的path爲key,以ActionMapping對象爲值放入actionConfigs這個Map中   
  9.       actionConfigs.put(config.getPath(), config);   
  10.       actionConfigList.add(config);   
  11.   
  12.   }  

步驟五  digester.addSetProperty
            ("struts-config/action-mappings/action/set-property",
             "property", "value");

       }
要使用action-mappings/action/set-property,需要編寫ActionMapping子類,並給action-mappings 標籤加上type屬性,屬性值就是我們編寫的字類。這樣當action的execute享有客戶操作時將得到我們編寫的ActionMapping事例。

我們編寫的ActionMapping要具有action-mappings/action/set-property所設置的所有屬性。如果action-mappings/action擁有set-property子標籤,那麼execute方法獲得的ActionMapping事例就擁有set-property所設置的屬性值。
一般來講我們都不會自己來擴展ActionMapping類,也就使得這個方法不是特別重要.

 


 

發佈了6 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章