写在最前面:说实在的,这部分会有点难懂,我自己也觉得很困难来把它整个说清楚,涉及到的类和方法都太宠杂,只能尽力而为.
因为本文主要是讨论使用digester进行xml解析.所以不关注,整个ActionServlet 初始化的整个流程,我们把注意力集中在解析xml配置文件的那部分代码,但读者还是要对Servlet,和Struts的一些相关的知识有一定的了解,比如说Servlet的生命周期,Struts的配置文件中的元素组成结构以及各个标签的含意是什么.
4.1 introduction of ActionServlet
形象点说,ActionServlet是Struts世界中的接线员,负责接收所有的请求,并把这个请求,转发给合适的人(这里的人在Struts中就是Action类型的实例了.
ActionServlet继承了,javax.servlet.http.HttpServlet.是个Servlet当然也就遵循Servlet的生命周期.
我们这里复习一下Servlet的生命周期
1.Servlet实例被创建.
2. 调用init(ServletConfig config )方法.(在HttpServlet中实际上在init(ServletConfig config ) 中调用了init()所以我们可以覆盖 init()方法来进行初例化)
3.接受客户请求.调用service方法.(在HttpServelt中,会调用doGet和doPost)
4. 调用destroy()方法.
5.实例被销毁.
接着,我们看一下,ActionServlet在生命周期都做了些什么事情呢?
init()方法
在struts源码中我增加了相关的注释,并删除了异常处理的代码,希望大家能更容易理解^^
- public void init() throws ServletException {
- //初使化国际化相关的,MessageResource bundle
- initInternal();
- //初使化我们这个controller servlet的字符转换函数
- initOther();
- //初使化web.xml中的信息.
- initServlet();
- //将ActionServlet实例以Globals.ACTION_SERVLET_KEY为key存在ServletContext对象中
- getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
- initModuleConfigFactory();
- // Initialize modules as needed
- //初使化,ModuleConfig这个moduleConfig相当重要,封装了所有的,struts-config.xml中的信息.
- //也就是我们解析struts-config.xml中的root元素了.一会我们将来看一下initModuleConfig中都做了些什么事情.
- ModuleConfig moduleConfig = initModuleConfig("", config);
- //通过moduleConfig中的配置文件信息,创建MessageResource对象.
- initModuleMessageResources(moduleConfig);
- //通过moduleConfig中的配置文件信息,创建DataSource对象.
- initModuleDataSources(moduleConfig);
- //通过moduleConfig中的配置文件信息,创建
- initModulePlugIns(moduleConfig);
- //将配置文件中的各个对象,设置成已配置状态.
- moduleConfig.freeze();
- Enumeration names = getServletConfig().getInitParameterNames();
- //通过web.xml的中参数配置,得到其它的Struts的配置文件并以上面的方式对,moduleConfig进行同样的配置
- 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();
- }
public void init() throws ServletException {
//初使化国际化相关的,MessageResource bundle
initInternal();
//初使化我们这个controller servlet的字符转换函数
initOther();
//初使化web.xml中的信息.
initServlet();
//将ActionServlet实例以Globals.ACTION_SERVLET_KEY为key存在ServletContext对象中
getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
initModuleConfigFactory();
// Initialize modules as needed
//初使化,ModuleConfig这个moduleConfig相当重要,封装了所有的,struts-config.xml中的信息.
//也就是我们解析struts-config.xml中的root元素了.一会我们将来看一下initModuleConfig中都做了些什么事情.
ModuleConfig moduleConfig = initModuleConfig("", config);
//通过moduleConfig中的配置文件信息,创建MessageResource对象.
initModuleMessageResources(moduleConfig);
//通过moduleConfig中的配置文件信息,创建DataSource对象.
initModuleDataSources(moduleConfig);
//通过moduleConfig中的配置文件信息,创建
initModulePlugIns(moduleConfig);
//将配置文件中的各个对象,设置成已配置状态.
moduleConfig.freeze();
Enumeration names = getServletConfig().getInitParameterNames();
//通过web.xml的中参数配置,得到其它的Struts的配置文件并以上面的方式对,moduleConfig进行同样的配置
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();
}
由上面的代码可以看出,在init方法中,Struts对很的信息进行了初使化,并使用,
ModuleConfig moduleConfig = initModuleConfig("", config)方法得到了struts-config.xml中的信息,所以解析配置文件的具体的方法也就是在这个方法里面完成的了.我们将在一会详细讲解一下这个方法的代码.
doGet 以及 doPost方法.
将请求委托给
process(request, response);方法.
我们再来看一下process方法又做了些什么呢?
- protected void process(HttpServletRequest request, HttpServletResponse response)
- throws IOException, ServletException {
- ModuleUtils.getInstance().selectModule(request, getServletContext());
- ModuleConfig config = getModuleConfig(request);
- //通过config对象来得来一个RequestProcessor对象(第一次请求的时候,为空的,).
- RequestProcessor processor = getProcessorForModule(config);
- if (processor == null) {
- //如果还没有配置一个RequestProcessor对象,就会通过,config对象来生成一个,默认就为org.apache.struts.action.RequestProcessor可以被扩展.
- processor = getRequestProcessor(config);
- }
- /**
- 通过processor来转发处理请求.(无非是通过请求的路径,以及config对象来生成
- ActionMapping BeanForm 以及,具体的Action实例,和 ActionForward)
- 然后调用得到的Action bean的execute方法
- */
- processor.process(request, response);
- }
protected void process(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
ModuleUtils.getInstance().selectModule(request, getServletContext());
ModuleConfig config = getModuleConfig(request);
//通过config对象来得来一个RequestProcessor对象(第一次请求的时候,为空的,).
RequestProcessor processor = getProcessorForModule(config);
if (processor == null) {
//如果还没有配置一个RequestProcessor对象,就会通过,config对象来生成一个,默认就为org.apache.struts.action.RequestProcessor可以被扩展.
processor = getRequestProcessor(config);
}
/**
通过processor来转发处理请求.(无非是通过请求的路径,以及config对象来生成
ActionMapping BeanForm 以及,具体的Action实例,和 ActionForward)
然后调用得到的Action bean的execute方法
*/
processor.process(request, response);
}
所有的请求最终都是通过,ActionServelt 的process方法中被RequestProcessor来处理的,
RequestProcessor通过ModuleConfig对象里的信息,以前客户端的请求的路径,分别得到,在配置文件中所配置的,Action bean的实例, ActionMapping的信息,ActionForward的信息,以及,可以所配置的FormBean的信息.然后调用 Action bean 实例的execute方法来完成整个请求.
最后再来看destory方法
- public void destroy() {
- /**
- * 和 init()方 法 相 对 应 ,做 了 相 关 的 清 理 工 作 ,具 体 的 ,读 者 可 以 参 见 init()方 法
- */
- if (log.isDebugEnabled()) {
- log.debug(internal.getMessage("finalizing"));
- }
- destroyModules();
- destroyInternal();
- getServletContext().removeAttribute(Globals.ACTION_SERVLET_KEY);
- // Release our LogFactory and Log instances (if any)
- ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
- if (classLoader == null) {
- classLoader = ActionServlet.class.getClassLoader();
- }
- LogFactory.release(classLoader);
- PropertyUtils.clearDescriptors();
- }
public void destroy() {
/**
* 和 init()方 法 相 对 应 ,做 了 相 关 的 清 理 工 作 ,具 体 的 ,读 者 可 以 参 见 init()方 法
*/
if (log.isDebugEnabled()) {
log.debug(internal.getMessage("finalizing"));
}
destroyModules();
destroyInternal();
getServletContext().removeAttribute(Globals.ACTION_SERVLET_KEY);
// Release our LogFactory and Log instances (if any)
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = ActionServlet.class.getClassLoader();
}
LogFactory.release(classLoader);
PropertyUtils.clearDescriptors();
}
这一节,我们详细的介绍了ActionServlet的生命周期中各个阶段所做的事情.下一节,我们将专注于,initModuleConfig方法,看一下struts只解析xml文件的具体过程.
4.2 concentrate on the method of initModuleConfig
上一节,我们讲了ActionServlet的生命周期,其中有一个方法进入了我们的视野.
ModuleConfig moduleConfig = initModuleConfig("", config) ,在这一节中我们将要仔细分析这个方法的源代码,
当然为了更容易理解,我将删除掉struts中源码的一部分,只保留核心那部分代码..
- protected ModuleConfig initModuleConfig(String prefix, String paths)
- throws ServletException {
- // Parse the configuration for this module
- ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
- /**
- 通过prefix的值来创建ModuleConfig对象
- 这里的prefix的含意是,模块名称,使用过struts的朋友可能有这个印象,在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();
- // 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;
- }
- //将 解 析 的 具 体 操 作 委 托 给 parseModuleConfigFile方 法
- this.parseModuleConfigFile(digester, path);
- }
- //将 所 得 到 的 ModuleConfig对 象 以 org.apache.struts.action.MODULE为 key放 进 ServletContext对 象 中 去
- //以 方 便 以 后 的 使 用
- getServletContext().setAttribute(
- Globals.MODULE_KEY + config.getPrefix(),
- config);
- return config;
- }
protected ModuleConfig initModuleConfig(String prefix, String paths)
throws ServletException {
// Parse the configuration for this module
ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
/**
通过prefix的值来创建ModuleConfig对象
这里的prefix的含意是,模块名称,使用过struts的朋友可能有这个印象,在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();
// 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;
}
//将 解 析 的 具 体 操 作 委 托 给 parseModuleConfigFile方 法
this.parseModuleConfigFile(digester, path);
}
//将 所 得 到 的 ModuleConfig对 象 以 org.apache.struts.action.MODULE为 key放 进 ServletContext对 象 中 去
//以 方 便 以 后 的 使 用
getServletContext().setAttribute(
Globals.MODULE_KEY + config.getPrefix(),
config);
return config;
}
整个方法中很明确,首先,通过,initConfigDigester();方法来得到一个,已经被设置了各种监听的Digester对象,然后将path路径下的xml 文件进行解析.那么,到底设置了哪些监听规则呢?我们来跟到initConfigDigester()方法中看一下.
(源码的版本依然被我阉割了,以便于大家理解.)
- protected Digester initConfigDigester() throws ServletException {
- //是否已经存在解析好了的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());
- // Return the completely configured Digester instance
- return (configDigester);
- }
protected Digester initConfigDigester() throws ServletException {
//是否已经存在解析好了的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());
// Return the completely configured Digester instance
return (configDigester);
}
从这个方法中,我们可以看到,具体的匹配规则是通过,一个叫ConfigRuleSet()的对象传进来的.
我们接着考查,到底这个ConfigRuleSet()是个啥鬼东西^^.
这个类大约有五百行大小,为了方便,我们在这里讲解,我们只截取其中的一部分,但这也足够大家理解了.
- public class ConfigRuleSet extends RuleSetBase {
- public void addRuleInstances(Digester digester) {
- //步骤一
- 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");
- }
- 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);
- }
- }
- }
- final class ActionMappingFactory extends AbstractObjectCreationFactory {
- 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;
- }
- }
- }
public class ConfigRuleSet extends RuleSetBase {
public void addRuleInstances(Digester digester) {
//步骤一
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");
}
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);
}
}
}
final class ActionMappingFactory extends AbstractObjectCreationFactory {
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;
}
}
}
下面我们就按照代码中注释的五个步骤一步一步分析.
步骤一. 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);
- }
- }
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;
- }
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对象.
看一下,addActionConfig方法的具体实现.
- 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);
- }
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类,也就使得这个方法不是特别重要.
4.3 小节
通过上一节,我们分析了ActionMapping的解析过程,当然在struts配置文件中,其它部分的解析方式实际上都是同这个类似的,大家完全可以举一反三的去看它的源代码.(重复性劳动没有啥意义SO.)