分析 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类,也就使得这个方法不是特别重要.

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值