设为首页 收藏本站
查看: 421|回复: 0

[经验分享] tomcat Digester分析

[复制链接]

尚未签到

发表于 2017-1-20 10:06:04 | 显示全部楼层 |阅读模式
  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。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-331055-1-1.html 上篇帖子: tomcat 无法启动 下篇帖子: Tomcat 内存管理
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表