/**
* <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>标签时,调用下面的方法
- 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);
- }
- }
我们看到它将取得<action-mappings >标签中的type元素的值并把它设置给配置文件的Root元素ModuleConfig的actionMappingClass,这个域的值默认为
org.apache.struts.action.ActionMapping
步骤二, digester.addFactoryCreate
("struts-config/action-mappings/action",
new ActionMappingFactory());
当遇到<action>标签时,将使用ActionMappingFactory类的createObject方法来创建一个对象.
- 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);
- } catch (Exception e) {
- digester.getLogger().error(
- "ActionMappingFactory.createObject: ", e);
- }
- return actionMapping;
- }
通过,<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对象.
- public void addActionConfig(ActionConfig config) {
- if (configured) {
- throw new IllegalStateException("Configuration is frozen");
- }
- config.setModuleConfig(this);
- //通过ActionMapping中的path为key,以ActionMapping对象为值放入actionConfigs这个Map中
- actionConfigs.put(config.getPath(), config);
- actionConfigList.add(config);
- }
步骤五 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类,也就使得这个方法不是特别重要.