注:本文的内容没有涉及每个细节,希望读者可以在阅读的同时翻看源码,文章的内容为作者个人理解,希望达到抛砖引玉的效果。也希望可以得到大家的拍砖,多做交流。
提起xml解析,脑海里第一反应一定是Dom,SAX等方式,刚开始学习Java的时候我也是使用这两种方式进行解析,拿到一个Xml第一反应一定是“又要写if-else了”。在学习Tomcat过程中发现了一个组件Digester,用于解析xml可谓简洁方便。
Digester是Jakarta 子项目Commons下的一个模块,支持基于规则的对任意XML文档的处理。它最初是Structs项目的一部分,后因其通用性而划归Commons。
Digester作为组件中一个核心的类,我们首先观看一下他的类结构图:
从图中可以看到Digester继承自DefaultHandler, 间接实现了EntityResolver,ErrorHandler, DTDHandler, ContentHandler四个接口,熟悉SAX的朋友应该了解这几个接口的功能。由此可以看出Digester是基于SAX原理,假设我们自己基于SAX设计Digester,我们该处理哪些问题?
Xml的格式千变万化以及带来的解析方式的变化,这是首先需要的解决的问题.
1 Xml永远是用户的Xml,所以只有让用户通过Digester制定符合自身Xml的规则链,Digester提供一套完备的规则链制定方法。
2 解析方式的变化是由Xml格式的变化带来的,所以Digester使用Rule接口封装,一个规则对应一种处理方式,如果用户不满意当前提供的默认处理方式,可以实现Rule接口自定义处理方式。
以上两个问题有了解决思路,就解决了主思路问题,现在我们来看一下Digester中是如何把两个问题处理好的。
首先,Digester类中提供了一系列规则链的制定方法,注意在编写规则链之前需要先调用push方法,将xml文档的根元素压入栈中。
每个规则链制定方法都会调用addRule, 例如添加创建对象方法。
addRule方法会将xml的正则表达式表达规则,和当前对应的规则加入到rules变量中,rules的类型为接口类型,Digester程序中使用到的是其实现类BasesRule,类结构图如下图:
通过registerRule方法,将当前规则和路径正则放入到RulesBase类中的HashMap cache 和 ArrayList rules 中。其中cache记录规则和路径正则表达式的一一对应关系,rules则记录了当前xml文件所涉及到的所有的规则。Rule接口封装了SAX中经典的start,body,end方法。
规则制定完毕之后则需要进入到解析的部分了。
调用Digester.parse 方法,则开始进行解析工作。parse中调用了configure方法,咋一看是进行配置信息的初始化工作,可是进入initialize方法之后发现,该方法并没有执行任何内容。查看注释明白一切,该方法提供延迟加载的钩子函数,同时Digester毫不吝啬的在注释中展示了可扩展性。
configure执行完毕后,进入核心的parser代码片段,getXMLReader方法中首先获取XmlParser,然后在获取XmlReader。由于之前介绍过Digester是基于SAX解析方式的,所以很容易想到SAXParserImpl类。SAXParserImpl类的构造方法中将xmlReader变量赋值为JAXPSAXParser类,则最终调用的parse方法为AbstractSAXParser类中的parse方法。
接下来就进入到我们熟悉的SAX模式解析Xml,这个我们就比较熟悉了,每次开始解析进入startElement,endElement, 而endElement方法中又包含调用每个规则的body方法和end方法。
个人觉得使用的是策略模式,然后调用到具体匹配的规则类中的实现方法。这种实现方法带来一个好处就是开发人员如果觉得Digester中提供的方法和规则实现类不足以满足个人要求,可以自定义Rule接口实现类完成需要的功能。
由于篇幅有限,将Digester的一部分内容进行了分析,还有需要细节需要大家学习的时候多去关注,比如Digester中每一层的调用之间都包含许多的校验,其中的设计模式也值得大家细心专研。建议大家订阅commons邮件列表,多和开发者交流。
如果文中有任何纰漏的地方,欢迎大家批评指正,欢迎拍砖。
Email:zqxjqka#gmail.com
QQ: 982080920