您的位置:首页 > 编程语言 > Java开发

Struts源码学习 — 初始化ModuleConfig

2008-01-25 13:46 351 查看
初始化ModuleConfig就是指Struts将struts-config.xml初始化为ModuleConfig对象,实现这个动作的代码在ActionServlet的init()方法中,如下:

Java代码
initModuleConfigFactory();
// Initialize modules as needed
ModuleConfig moduleConfig = initModuleConfig("", config);
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();

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

initModuleConfigFactory();
// Initialize modules as needed
ModuleConfig moduleConfig = initModuleConfig("", config);
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();

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();实际上,初始化ModuleConfig对象的过程仅仅在第3行的那句代码里,这里之所以列出这么多代码,是因为这段代码所做的事情可看成一个整体。对于这段代码的概括描述可参见Struts源码学习 — ActionServlet的初始化

将第3行代码里的initModuleConfig()方法挖开,如下:

Java代码
/**
* <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)
*
* @exception ServletException if initialization cannot be performed
* @since Struts 1.1
*/
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
+ "'");
}

// Parse the configuration for this module
ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
ModuleConfig config = factoryObject.createModuleConfig(prefix);

// Configure the Digester instance we will use
Digester digester = initConfigDigester();

// Process each specified resource path
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);
}

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

// Force creation and registration of DynaActionFormClass instances
// for all dynamic form beans we wil be using
FormBeanConfig fbs[] = config.findFormBeanConfigs();
for (int i = 0; i < fbs.length; i++) {
if (fbs[i].getDynamic()) {
fbs[i].getDynaActionFormClass();
}
}

return 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)
*
* @exception ServletException if initialization cannot be performed
* @since Struts 1.1
*/
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
+ "'");
}

// Parse the configuration for this module
ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
ModuleConfig config = factoryObject.createModuleConfig(prefix);

// Configure the Digester instance we will use
Digester digester = initConfigDigester();

// Process each specified resource path
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);
}

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

// Force creation and registration of DynaActionFormClass instances
// for all dynamic form beans we wil be using
FormBeanConfig fbs[] = config.findFormBeanConfigs();
for (int i = 0; i < fbs.length; i++) {
if (fbs[i].getDynamic()) {
fbs[i].getDynaActionFormClass();
}
}

return config;
}

首先27、28两行,创建ModuleConfigFactory对象,其默认的实现是org.apache.struts.config.impl.DefaultModuleConfigFactory,可在web.xml中通过设置ActionServlet的configFactory初始化参数来改变实现类。然后由ModuleConfigFactory创建ModuleConfig对象,DefaultModuleConfigFactory的实现就是new一个org.apache.struts.config.impl.ModuleConfigImpl的对象,以prefix为构造方法的参数。

然后初始化Digester对象(后面详细说)。

因为参数paths可以是以逗号为分隔符的,多个struts-config.xml文件的路径,所以接下来的循环是用Digester对象分别对这多个struts-config.xml文件进行解析,解析的过程会将每个struts-config.xml文件里的元素都初始化为相应的XXXConfig对象,并将这些对象装进之前创建的ModuleConfig对象里。

然后将这个ModuleConfig对象放到ServletContext里。

59-64行,这一段是检查struts-config.xml文件里的所有form-bean元素,如果其type属性指定的类是动态FormBean,即org.apache.struts.action.DynaActionForm类或它的子类,就初始化FormBeanConfig的dynaActionFormClass字段。

最重要的代码在31行的initConfigDigester()方法里,挖开如下:

Java代码
/**
* <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>
*
* @exception 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?
if (configDigester != null) {
return (configDigester);
}

// Create a new Digester instance with standard capabilities
configDigester = new Digester();
configDigester.setNamespaceAware(true);
configDigester.setValidating(this.isValidating());
configDigester.setUseContextClassLoader(true);
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);
}

/**
* <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>
*
* @exception 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?
if (configDigester != null) {
return (configDigester);
}

// Create a new Digester instance with standard capabilities
configDigester = new Digester();
configDigester.setNamespaceAware(true);
configDigester.setValidating(this.isValidating());
configDigester.setUseContextClassLoader(true);
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);
}

这个方法初始化Digester对象,就是这个Digester将struts-config.xml解析为ModuleConfig对象的。

Digester类的完全限定名为org.apache.commons.digester.Digester,来自于Apache Commons Digester,它是一个将XML映射到Java对象的工具。该工具底层还是使用SAX来解析XML,只是它在SAX的基础上,提出了一个Rule的概念。所谓Rule,通俗的解释就是“当扫描XML遇到某些结点时要做的事情”。Digester类根据这些Rule来解析XML,因此,解析之前需要给Digester添加这些Rule,Digester类里定义了一系列方法用来直接添加Rule。

也可以将添加Rule的繁琐工作委托给RuleSet来做,然后Digester调用addRuleSet(RuleSet)方法就行了。上面代码的25行就是这样做的,如何将struts-config.xml解析为ModuleConfig对象的Rule就是在ConfigRuleSet类的addRuleInstances()方法里添加到Digester的,该方法代码如下:

Java代码
/**
* <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) {

digester.addObjectCreate
("struts-config/data-sources/data-source",
"org.apache.struts.config.DataSourceConfig",
"className");
digester.addSetProperties
("struts-config/data-sources/data-source");
digester.addSetNext
("struts-config/data-sources/data-source",
"addDataSourceConfig",
"org.apache.struts.config.DataSourceConfig");

digester.addRule
("struts-config/data-sources/data-source/set-property",
new AddDataSourcePropertyRule());

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

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

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

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.addSetProperty
("struts-config/action-mappings/action/exception/set-property",
"property", "value");

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

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

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.addSetProperty
("struts-config/controller/set-property",
"property", "value");

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

digester.addFactoryCreate
("struts-config/form-beans/form-bean",
new ActionFormBeanFactory());
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.addSetProperty
("struts-config/form-beans/form-bean/form-property/set-property",
"property", "value");

digester.addSetProperty
("struts-config/form-beans/form-bean/set-property",
"property", "value");

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.addSetProperty
("struts-config/global-exceptions/exception/set-property",
"property", "value");

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

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

digester.addSetProperty
("struts-config/global-forwards/forward/set-property",
"property", "value");

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.addSetProperty
("struts-config/message-resources/set-property",
"property", "value");

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

}

/**
* <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) {

digester.addObjectCreate
("struts-config/data-sources/data-source",
"org.apache.struts.config.DataSourceConfig",
"className");
digester.addSetProperties
("struts-config/data-sources/data-source");
digester.addSetNext
("struts-config/data-sources/data-source",
"addDataSourceConfig",
"org.apache.struts.config.DataSourceConfig");

digester.addRule
("struts-config/data-sources/data-source/set-property",
new AddDataSourcePropertyRule());

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

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

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

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.addSetProperty
("struts-config/action-mappings/action/exception/set-property",
"property", "value");

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

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

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.addSetProperty
("struts-config/controller/set-property",
"property", "value");

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

digester.addFactoryCreate
("struts-config/form-beans/form-bean",
new ActionFormBeanFactory());
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.addSetProperty
("struts-config/form-beans/form-bean/form-property/set-property",
"property", "value");

digester.addSetProperty
("struts-config/form-beans/form-bean/set-property",
"property", "value");

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.addSetProperty
("struts-config/global-exceptions/exception/set-property",
"property", "value");

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

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

digester.addSetProperty
("struts-config/global-forwards/forward/set-property",
"property", "value");

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.addSetProperty
("struts-config/message-resources/set-property",
"property", "value");

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

}

这里的所有addXXX()方法都是在添加Rule,只不过其中不叫addRule的方法是添加Digester的内置Rule的便捷方法。所有方法的第一个参数都是一个XPath格式的String,这些String看着都很眼熟吧,没错,都是struts-config.xml里的。

先说说Digester是如何使用Rule来解析XML的,执行Digester类的parse()方法来扫描解析XML,当扫描XML遇到匹配某个XPath的标签,就开始使用Digester里对应于这个XPath的Rule。碰到开始标签时,调用Rule的begin()方法;标签如果有体,调用Rule的body()方法;碰到结束标签时,调用Rule的end()方法。如果对应于某个XPath有多个Rule,就有个调用顺序的问题了。对于begin()方法,是按照Rule添加到Digester里的顺序调用;end()方法调用顺序与begin()相反。这点跟栈的思想一样。

这段代码里使用到的一些Digester内置Rule有:

ObjectCreateRule:创建对象并压栈。

FactoryCreateRule:由工厂来创建对象并压栈。

SetPropertiesRule:将标签属性值设置到栈顶对象的属性里。

SetPropertyRule:设置栈顶对象的单个属性的值。

SetNextRule:调用栈顶第2个元素的指定方法来建立它和栈顶元素的关系。

一般对标签的处理方式是,先用ObjectCreateRule或FactoryCreateRule创建对象,然后用SetPropertiesRule或SetPropertyRule设置对象的属性值,最后用SetNextRule建立该标签和父标签的关系。上面的代码基本就是这个套路,另外还加入了一些Struts自定义的Rule,如下:

AddDataSourcePropertyRule:设置DataSource的属性。

PlugInSetPropertyRule:设置PlugIn的属性。

SetActionMappingClassRule:根据struts-config/action-mappings标签的type属性,设置ActionConfig的实现类。

SetActionFormBeanClassRule:根据struts-config/form-beans标签的type属性,设置FormBeanConfig的实现类。

SetActionForwardClassRule:根据struts-config/global-forwards标签的type属性,设置ForwardConfig的实现类。

对于ActionConfig、FormBeanConfig、ForwardConfig,有3种方式可以改变这3个XXXConfig的实现类。

1. 使用自定义的ModuleConfig实现类,在其中定义XXXConfig的实现类,这个改变影响范围最大,针对于整个应用的XXXConfig。Struts默认的ModuleConfig实现类是org.apache.struts.config.impl.ModuleConfigImpl,这个类里面定义了XXXConfig的默认实现类,分别是org.apache.struts.action.ActionMapping、org.apache.struts.action.ActionFormBean、org.apache.struts.action.ActionForward。

2. 象上面最后的3个Rule说明中提到的那样,设置type属性,这个改变只针对于单个struts-config.xml中的XXXConfig。

3. 具体到单个struts-config.xml中的单个XXXConfig配置,设置它的className属性,来改变单个XXXConfig的实现类,这个改变影响最小,只针对单个XXXConfig。

正因为它们的实现类可以这样灵活的改变,所以上面代码在为Digester添加创建它们实例的Rule时,使用的是FactoryCreateRule,而不是ObjectCreateRule。要构造一个FactoryCreateRule,需要为它提供一个ObjectCreationFactory,前者将创建对象的工作委托给后者。Struts提供了创建这3种Config对象的ObjectCreationFactory——ActionMappingFactory、ActionFormBeanFactory、ActionForwardFactory、GlobalForwardFactory。如果标签给出了className属性,这些Factory就以该属性值创建对象;如果没有,就以之前设置的实现类创建对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: