接着上一篇。这篇我们来分析有关匹配模式规则配置,及规则管理的源码。
匹配模式(matching pattern)的规则(rule)是如何配置上去的,以创建对象规则ObjectCreateRule为例,如下:
//指明匹配模式和要创建的类 digester.addObjectCreate( "students/student", Student.class);
F5进入下,可以看到:
/**
* Add an "object create" rule for the specified parameters.
*
* @param pattern Element matching pattern
* @param clazz Java class to be created
* @see ObjectCreateRule
*/
public void addObjectCreate(String pattern, Class clazz) {
addRule(pattern,
new ObjectCreateRule(clazz));
}
addObjectCreate()方法内部创建一个以Student.class(类型对象)作为构造方法参数的ObjectCreateRule对象,然后连同匹配模式pattern一起作为参数调用addRule()方法。addRule()方法是Digester内专门用于为匹配模式配置规则的方法,它的第一个参数
是匹配模式,第二个参数是规则,即ObjectCreateRule是个规则,查看源码可以看到ObjectCreateRule继承了org.apache.commons.digester.Rule类,所有继承该类的都是规则。ObjectCreateRule规则的作用是生成对象的,参数clazz就是要生成的对象。F5进入addRule():
/**
* <p>Register a new Rule matching the specified pattern.
* This method sets the <code>Digester</code> property on the rule.</p>
*
* @param pattern Element matching pattern
* @param rule Rule to be registered
*/
public void addRule(String pattern, Rule rule) {
rule.setDigester(this);
getRules().add(pattern, rule);
}
rule.setDigester(this); 将自身添加到rule的digester属性中,以后会有用。getRules().add(pattern, rule); 首先是得到Digester的规则库(用于存放所有规则的地方),然后将当前规则add 进去。 F5进入getRules():
/**
* Return the <code>Rules</code> implementation object containing our
* rules collection and associated matching policy. If none has been
* established, a default implementation will be created and returned.
*/
public Rules getRules() {
if (this.rules == null) {
this.rules = new RulesBase();
this.rules.setDigester(this);
}
return (this.rules);
}
首相判断下this.rules(规则库)是否是空的,不是则直接返回规则库,是则创建个新的。this.rules = new RulesBase();由这句可以看到规则库实际上是个RulesBase实例,进入RulesBase这个类看下:
public class RulesBase implements Rules {
protected HashMap cache = new HashMap();
protected Digester digester = null;
protected String namespaceURI = null;
protected ArrayList rules = new ArrayList();
public void add(String pattern, Rule rule) {
.
.
.
}
public List match(String namespaceURI, String pattern) {
.
.
.
}
protected List lookup(String namespaceURI, String pattern) {
.
.
.
}
}
上面的代码是我经过整理的。其中有个属性"cache",这个就是上面所说的规则库(rules),是个哈希表。它的key值就是匹配模式(matching pattern),value值是个List,存放这个匹配模式下的所有规则。可以看到匹配模式的真正作用是作为key值的。
其中还有个属性"rules",它和cache一样也是存放所有规则的,区别是它的类型是个有序表(ArrayList),在Digester里它只有一个作用,当Digester执行到endDocument()时,这时所有的规则同时都要执行finish()方法,如果遍历cache,由于cache是哈希表,是无序的,不能保证按先进先执行的规律执行,所以又单独创建了rules属性。
再来看下它的add()方法,如下:
/**
* Register a new Rule instance matching the specified pattern.
*
* @param pattern Nesting pattern to be matched for this Rule
* @param rule Rule instance to be registered
*/
public void add(String pattern, Rule rule) {
// to help users who accidently add '/' to the end of their patterns
int patternLength = pattern.length();
if (patternLength>1 && pattern.endsWith("/")) {
pattern = pattern.substring(0, patternLength-1);
}
List list = (List) cache.get(pattern);
if (list == null) {
list = new ArrayList();
cache.put(pattern, list);
}
list.add(rule);
rules.add(rule);
if (this.digester != null) {
rule.setDigester(this.digester);
}
if (this.namespaceURI != null) {
rule.setNamespaceURI(this.namespaceURI);
}
}
前三句的作用是去掉匹配模式最后的"/",如果有的话。即将"students/student/"变为"students/student"。第四句是根据匹配模式pattern从cache中找到该模式下的用于存放规则的list,如果没有则创建,然后将规则add到list中,同时也add到rules属性中。最下面俩句if 是设置rule, 有关命名空间(Namespace)的设置我就不说了,因为如果只是用Digester的内置规则的话,命名空间是用不到的,除非自己创建新的规则。另外注意:cache的value是ArrayList实例,所以规则的执行是严格按序的,即按添加进去的先后顺序的。
/**
* Return a List of all registered Rule instances that match the specified
* nesting pattern, or a zero-length List if there are no matches. If more
* than one Rule instance matches, they <strong>must</strong> be returned
* in the order originally registered through the <code>add()</code>
* method.
*
* @param namespaceURI Namespace URI for which to select matching rules,
* or <code>null</code> to match regardless of namespace URI
* @param pattern Nesting pattern to be matched
*/
public List match(String namespaceURI, String pattern) {
// List rulesList = (List) this.cache.get(pattern);
List rulesList = lookup(namespaceURI, pattern);
if ((rulesList == null) || (rulesList.size() < 1)) {
// Find the longest key, ie more discriminant
String longKey = "";
Iterator keys = this.cache.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
if (key.startsWith("*/")) {
if (pattern.equals(key.substring(2)) ||
pattern.endsWith(key.substring(1))) {
if (key.length() > longKey.length()) {
// rulesList = (List) this.cache.get(key);
rulesList = lookup(namespaceURI, key);
longKey = key;
}
}
}
}
}
if (rulesList == null) {
rulesList = new ArrayList();
}
return (rulesList);
}
match()是个匹配方法,实际上就是get。第一句是通过命名空间(namespaceURI)、匹配模式(pattern)来检索规则rulesList,通过lookup()方法,这个方法会在下文讲解。如果检索出则直接返回,否则需要进一步进行模糊查找,也就是通过通配符(*)。
Iterator keys = this.cache.keySet().iterator(); while (keys.hasNext()) {....} 遍历cache的key值,也就是遍历所有匹配模式。if (key.startsWith("*/")) {...} 判断匹配模式是否有以"*/"为开头的,由此可知"*"是个通配符。如果有则进一步判断。
if (pattern.equals(key.substring(2)) || pattern.endsWith(key.substring(1))) {...} 由这句话可知,通配符"*"可以取空值或任意字符,"pattern.equals(key.substring(2))" 判断当通配符代表空值时,"pattern.endsWith(key.substring(1))"判断通配符代表
非空字符时。if (key.length() > longKey.length()){...} 这句话的目的是从所有满足条件的匹配模式中选出字符长度最大的,因为字符越长所表示的匹配模式越趋于精确,如:当前pattern为"students/student/name", cache中有俩个带有通配符的匹配模式 "*/name" 、"*/students/student/name" 很显然"*/students/student/name" 更准确些。
/**
* Return a List of Rule instances for the specified pattern that also
* match the specified namespace URI (if any). If there are no such
* rules, return <code>null</code>.
*
* @param namespaceURI Namespace URI to match, or <code>null</code> to
* select matching rules regardless of namespace URI
* @param pattern Pattern to be matched
*/
protected List lookup(String namespaceURI, String pattern) {
// Optimize when no namespace URI is specified
List list = (List) this.cache.get(pattern);
if (list == null) {
return (null);
}
if ((namespaceURI == null) || (namespaceURI.length() == 0)) {
return (list);
}
// Select only Rules that match on the specified namespace URI
ArrayList results = new ArrayList();
Iterator items = list.iterator();
while (items.hasNext()) {
Rule item = (Rule) items.next();
if ((namespaceURI.equals(item.getNamespaceURI())) ||
(item.getNamespaceURI() == null)) {
results.add(item);
}
}
return (results);
}
lookup()执行具体的检索工作。第一句直接通过pattern从cache中得到此匹配模式下的所有规则,如果该pattern下没有配置规则,则直接返回null。
接下来这几句是对检索出的规则进行命名空间的验证。 由源码可知,当RulesBase、规则(Rule)同时设置了命名空间时,才进行命名空间的一致性验证。否则是不验证的。
总结下上面为匹配模式"students/student"添加ObjectCreateRule规则的过程:
调用digester.addObjectCreate( "students/student", Student.class)后, Digester创建ObjectCreateRule实例,调用规则配置方法addRule()将规则添加到规则库RulesBase中,规则库RulesBase通过add()方法在将规则添加进cache中。由于cache是个
哈希表,这样当系统解析时就可以通过匹配模式检索出其下的所有规则。
待续