tomcat中使用Digester进行xml to javaobject的转换,下面分析下其实现原理
Digester类继承自DefaultHandler,当xmlreader进行处理XML的时候,将事件通知指向Digester类来处理
那么当reader进行startElement 和endElemnet的时候对Digester进行通知进行相应的处理
例如:
<server>
<listener />
<service />
</server>
那么处理逻辑可以这来解释当遇到begin server的时候 创建server对象,当遇到listener时候创建Listener对象,当遇到Listener end的时候进行server.addlistener操作
但是这样的逻辑太当换了类了后,程序是不会自己去识别对应的类(也是不可能识别的) 所以如果想要支持你所想支持的类那么必须要自己进行配置,这里Digester框架使用了策略模式。
那么先看一段源码
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResources");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResources");
可以看出在使用Digester 你必须要自己告诉程序当遇到不同的元素要进行怎样的处理
上述为当遇到Server元素的时候,进行org.apache.catalina.core.StandardServer的穿件,并赋予属性给类的属性,并将StandardServer添加到根元素。
那么其中的内部实现其实是这样的
在Digester类中实现了startElement方法以及end方法等,当reader通知Digester 后调用startElment后逐个调用每个元素自己的规则来进行处理,所以就有了上面的一段代码进行规则制定,那么这些规则又是怎么实现的
下面是Rule的基类
public abstract class Rule {
/**
* This method is called when the beginning of a matching XML element
* is encountered. The default implementation delegates to the deprecated
* method {@link #begin(Attributes) begin} without the
* <code>namespace</code> and <code>name</code> parameters, to retain
* backwards compatibility.
*
* @param namespace the namespace URI of the matching element, or an
* empty string if the parser is not namespace aware or the element has
* no namespace
* @param name the local name if the parser is namespace aware, or just
* the element name otherwise
* @param attributes The attribute list of this element
* @since Digester 1.4
*/
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
begin(attributes);
}
/**
* This method is called when the body of a matching XML element
* is encountered. If the element has no body, this method is
* not called at all.
*
* @param text The text of the body of this element
* @deprecated Use the {@link #body(String,String,String) body} method
* with <code>namespace</code> and <code>name</code> parameters
* instead.
*/
@Deprecated
public void body(String text) throws Exception {
// The default implementation does nothing
}
/**
* This method is called when the body of a matching XML element is
* encountered. If the element has no body, this method is not called at
* all. The default implementation delegates to the deprecated method
* {@link #body(String) body} without the <code>namespace</code> and
* <code>name</code> parameters, to retain backwards compatibility.
*
* @param namespace the namespace URI of the matching element, or an
* empty string if the parser is not namespace aware or the element has
* no namespace
* @param name the local name if the parser is namespace aware, or just
* the element name otherwise
* @param text The text of the body of this element
* @since Digester 1.4
*/
public void body(String namespace, String name, String text)
throws Exception {
body(text);
}
/**
* This method is called when the end of a matching XML element
* is encountered.
*
* @deprecated Use the {@link #end(String,String) end} method with
* <code>namespace</code> and <code>name</code> parameters instead.
*/
@Deprecated
public void end() throws Exception {
// The default implementation does nothing
}
/**
* This method is called when the end of a matching XML element
* is encountered. The default implementation delegates to the deprecated
* method {@link #end end} without the
* <code>namespace</code> and <code>name</code> parameters, to retain
* backwards compatibility.
*
* @param namespace the namespace URI of the matching element, or an
* empty string if the parser is not namespace aware or the element has
* no namespace
* @param name the local name if the parser is namespace aware, or just
* the element name otherwise
* @since Digester 1.4
*/
public void end(String namespace, String name)
throws Exception {
end();
}
/**
* This method is called after all parsing methods have been
* called, to allow Rules to remove temporary data.
*/
public void finish() throws Exception {
// The default implementation does nothing
}
}
主要四个方法 begin 、body、end、finish
而对于元素的处理便会先进行rule的选取,然后对每个rule进行上面四个方法的处理,例如:addObjectCreate的方法实现为
public void addObjectCreate(String pattern, String className,
String attributeName) {
addRule(pattern,
new ObjectCreateRule(className, attributeName));
}
当对SERVER元素进行addObjectCreate的时候实际上等于是给SERVER添加了一个RULE,那么整个处理过程为怎么回事呢,首先贴出Digester类中中startElement方法(只贴出主要部分)
if ((rules != null) && (rules.size() > 0)) {
for (int i = 0; i < rules.size(); i++) {
try {
Rule rule = rules.get(i);
if (debug) {
log.debug(" Fire begin() for " + rule);
}
rule.begin(namespaceURI, name, list);
} catch (Exception e) {
log.error("Begin event threw exception", e);
throw createSAXException(e);
} catch (Error e) {
log.error("Begin event threw error", e);
throw e;
}
}
} else {
if (debug) {
log.debug(" No rules found matching '" + match + "'.");
}
}
可以看出实际上处理元素开始就是筛选处理的元素的对应的RULE,进行begin方法的调用
而endElement方法思路也基本是这样只不过是调用了RULE的body和end方法,而finish()方法是当XML文档reader完成后进行调用。并且当在startElement方法中调用RULE的时候是按照添加RULE的先后顺序进行调用,EndElement方法则是想法的顺序进行调用,谁让xml就是这么个规则。
最后挑选其中的一个RULE的子类(具体实现类)的代码分析,当然有N个这样的子类,下面贴出ObjectCreateRule的部分代码,也就是当调用Digester.addObjectCreate方法后添加的RULE
首先贴出begin代码
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
// Identify the name of the class to instantiate
String realClassName = className;
if (attributeName != null) {
String value = attributes.getValue(attributeName);
if (value != null) {
realClassName = value;
}
}
if (digester.log.isDebugEnabled()) {
digester.log.debug("[ObjectCreateRule]{" + digester.match +
"}New " + realClassName);
}
if (realClassName == null) {
throw new NullPointerException("No class name specified for " +
namespace + " " + name);
}
// Instantiate the new object and push it on the context stack
Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
Object instance = clazz.newInstance();
digester.push(instance);
}
可以看出其中的逻辑为如果有classname这个属性的话,那么这个对象instance的时候 要进行子类的instance,并且把instance的对象方法stack中,digester.push方法实际上就是放入Digester类的stack字段中,而整个Digester对类的操作都是在stack中进行的,当然这个原因也是xml的规则所决定的。
那么贴出ObjectCreateRule的end代码
public void end(String namespace, String name) throws Exception {
Object top = digester.pop();
if (digester.log.isDebugEnabled()) {
digester.log.debug("[ObjectCreateRule]{" + digester.match +
"} Pop " + top.getClass().getName());
}
}
可以看出instance进行了出栈操作
当然还有其他很多的RULE,比如属性赋值,添加child等等。当然既然知道了RULE的实现也可以自我定制自我的RULE。