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。