接着上一篇。这篇我们来分析有关匹配模式规则配置,及规则管理的源码。
匹配模式(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是个
哈希表,这样当系统解析时就可以通过匹配模式检索出其下的所有规则。
待续