degister In Action (关于struts框架中的xml解析) part3

[size=large]CH4. 分析 Struts中的struts-config.xml解析过程[/size]

写在最前面:说实在的,这部分会有点难懂,我自己也觉得很困难来把它整个说清楚,涉及到的类和方法都太宠杂,只能尽力而为.

因为本文主要是讨论使用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();

}

由上面的代码可以看出,在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);
}

所有的请求最终都是通过,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();

}

这一节,我们详细的介绍了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;
}




整个方法中很明确,首先,通过,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);
}


从这个方法中,我们可以看到,具体的匹配规则是通过,一个叫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;
}

}

}


下面我们就按照代码中注释的五个步骤一步一步分析.

步骤一. 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对象.
看一下,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);

}

步骤五 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.)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值