真正的server.xml文件中包含很多内容,但只要连接其规则,就能从server.xml文件中解读出server对象的内容。
从使用示例中分析.
代码
源码地址
xml文件:
<?xml version="1.0" encoding="ISO-8859-1"?>
<employee firstName="Freddie" lastName="Mercury">
<office description="Headquarters">
<address streetName="Wellington Avenue" streetNumber="223"/>
</office>
<office description="Client site">
<address streetName="Downing Street" streetNumber="10"/>
</office>
</employee>
对象类:
public class Employee {
private String firstName;
private String lastName;
private ArrayList offices = new ArrayList();
public Employee() {
System.out.println("Creating Employee");
}
//TODO getter setter 方法
public void printName() {
System.out.println("My name is " + firstName + " " + lastName);
}
}
public class Office {
private Address address;
private String description;
public Office() {
System.out.println("..Creating Office");
}
//TODO getter setter 方法
}
public class Address {
private String streetName;
private String streetNumber;
public Address() {
System.out.println("....Creating Address");
}
// getter setter 方法
public String toString() {
return "...." + streetNumber + " " + streetName;
}
}
测试类
public class Test02 {
public static void main(String[] args) {
String path = System.getProperty("user.dir") + File.separator +
"/my-tomcat/src/main/resources/chapter15";
File file = new File(path, "employee2.xml");
Digester digester = new Digester();
// add rules
digester.addObjectCreate("employee", "com.tomcat.chapter15.digestertest.Employee");
digester.addSetProperties("employee");
digester.addObjectCreate("employee/office", "com.tomcat.chapter15.digestertest.Office");
digester.addSetProperties("employee/office");
digester.addSetNext("employee/office", "addOffice");
digester.addObjectCreate("employee/office/address",
"com.tomcat.chapter15.digestertest.Address");
digester.addSetProperties("employee/office/address");
digester.addSetNext("employee/office/address", "setAddress");
parseAndTest(digester, file);
}
public static void parseAndTest(Digester digester, File file) {
try {
Employee employee = (Employee) digester.parse(file);
ArrayList offices = employee.getOffices();
Iterator iterator = offices.iterator();
System.out.println("###################################################");
while (iterator.hasNext()) {
Office office = (Office) iterator.next();
Address address = office.getAddress();
System.out.println(office.getDescription());
System.out.println("Address : " +
address.getStreetNumber() + " " + address.getStreetName());
System.out.println("--------------------------------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果
Creating Employee
Setting lastName : Mercury
Setting firstName : Freddie
..Creating Office
..Setting office description : Headquarters
....Creating Address
....Setting streetNumber : 223
....Setting streetName : Wellington Avenue
..Setting office address : ....223 Wellington Avenue
Adding Office to this employee
..Creating Office
..Setting office description : Client site
....Creating Address
....Setting streetNumber : 10
....Setting streetName : Downing Street
..Setting office address : ....10 Downing Street
Adding Office to this employee
###################################################
Headquarters
Address : 223 Wellington Avenue
--------------------------------------------------
Client site
Address : 10 Downing Street
--------------------------------------------------
归纳下使用步骤:
- 创建digester对象;
- 在digester中添加解析规则;
- 调用digester.parse(file), 就会返回对象。
分析规则对应的操作
// 遇到employee标签,则创建"com.tomcat.chapter15.digestertest.Employee"对象
digester.addObjectCreate("employee", "com.tomcat.chapter15.digestertest.Employee");
// 为"com.tomcat.chapter15.digestertest.Employee"对象添加属性(<employee firstName="Freddie" lastName="Mercury"> 即将firstName和lastName的值赋给新建的对象)
digester.addSetProperties("employee");
// 调用新建对象的printName()方法
digester.addCallMethod("employee", "printName");
// 遇到employee下的office标签,则创建"com.tomcat.chapter15.digestertest.Office"对象
digester.addObjectCreate("employee/office", "com.tomcat.chapter15.digestertest.Office");
digester.addSetProperties("employee/office");
//这个是复制操作,将当前的对象通过addOffice方法赋给(简单理解的话就是外层对象)
digester.addSetNext("employee/office", "addOffice");
// ......
通过上面的规则,就可以在遇到xml中不同标签时,将其变成我们想要的对象。
源码分析
Digester类包含一个protected Rules rules = null; protected ArrayStack stack = new ArrayStack();
public class Digester extends DefaultHandler {
//用来存储添加的各种规则
protected Rules rules = null;
//压入每次产生的新对象, 对于前面提到的addSetNext规则,就会用到这个堆栈
protected ArrayStack stack = new ArrayStack();
public Rules getRules() {
if (this.rules == null) {
this.rules = new RulesBase();
this.rules.setDigester(this);
}
return (this.rules);
}
//...
//所有的规则,都是一样,会调用addRule(...)加入到rules中;
public void addObjectCreate(String pattern, String className) {
addRule(pattern, new ObjectCreateRule(className));
}
public void addRule(String pattern, Rule rule) {
rule.setDigester(this);
getRules().add(pattern, rule);
}
//因为传入文件类型不同,有多个重载函数, 但流程都是大同小异的
public Object parse(InputStream input) throws IOException, SAXException {
configure();
InputSource is = new InputSource(input);
getXMLReader().parse(is);
cleanup();
return (root);
}
}
所有的规则,都会调用addRule()方法,将规则rule加入到RuleBase的map中;
解析流程:
-
配置自身类,initialize(), 实际误操作;
-
获取配置文件的字节流;set其systemId(不知道有撒用);
-
获取xmlReader(解析xml文件用的类)
-
xml.parse(input…), 关键的一步,分析xml文件,涉及的内容(一层包一层),但需要知道:
当解析到标签时,digester类会从ruleBase中匹配出XXX对应的一系列规则;然后依次执行rule的begin()函数, 并将XXX匹配的规则加入到一个使用记录堆栈,public void startElement(String namespaceURI, String localName, String qName, Attributes list) throws SAXException { boolean debug = log.isDebugEnabled(); //.... // Fire "begin" events for all relevant rules //找出匹配规则 List rules = getRules().match(namespaceURI, match); matches.push(rules); //顺序执行begin()函数 if ((rules != null) && (rules.size() > 0)) { Substitutor substitutor = getSubstitutor(); if (substitutor!= null) { list = substitutor.substitute(list); } for (int i = 0; i < rules.size(); i++) { try { Rule 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的end()函数,
public void endElement(String namespaceURI, String localName, String qName) throws SAXException { //... // Fire "body" events for all relevant rules // 使用记录堆栈弹出对应的规则集 List rules = (List) matches.pop(); if ((rules != null) && (rules.size() > 0)) { String bodyText = this.bodyText.toString(); Substitutor substitutor = getSubstitutor(); if (substitutor!= null) { bodyText = substitutor.substitute(bodyText); } for (int i = 0; i < rules.size(); i++) { try { Rule rule = (Rule) rules.get(i); if (debug) { log.debug(" Fire body() for " + rule); } rule.body(namespaceURI, name, bodyText); } catch (Exception e) { log.error("Body event threw exception", e); throw createSAXException(e); } catch (Error e) { log.error("Body event threw error", e); throw e; } } } else { if (debug) { log.debug(" No rules found matching '" + match + "'."); } } // Recover the body text from the surrounding element bodyText = (StringBuffer) bodyTexts.pop(); if (debug) { log.debug(" Popping body text '" + bodyText.toString() + "'"); } // Fire "end" events for all relevant rules in reverse order if (rules != null) { for (int i = 0; i < rules.size(); i++) { // 倒序执行的代码 int j = (rules.size() - i) - 1; try { Rule rule = (Rule) rules.get(j); if (debug) { log.debug(" Fire end() for " + rule); } rule.end(namespaceURI, name); } catch (Exception e) { log.error("End event threw exception", e); throw createSAXException(e); } catch (Error e) { log.error("End event threw error", e); throw e; } } } // Recover the previous match expression int slash = match.lastIndexOf('/'); if (slash >= 0) { match = match.substring(0, slash); } else { match = ""; } }
例如ObjectCreateRule就对应加载所需类实例化并压入前面提到的堆栈
public void begin(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); } // Instantiate the new object and push it on the context stack // 加载类 Class clazz = digester.getClassLoader().loadClass(realClassName); // 实例化 Object instance = clazz.newInstance(); // 进栈 digester.push(instance); }
下面是ObjectCreateRule对应的end()函数, 也就是出栈。
public void end() throws Exception { Object top = digester.pop(); if (digester.log.isDebugEnabled()) { digester.log.debug("[ObjectCreateRule]{" + digester.match + "} Pop " + top.getClass().getName()); } }
特别提下setNextRule,其begin()和end()如下:
//begin函数使用的是其父类Rule的begin方法,什么都不操作 public void end() throws Exception { // Identify the objects to be used //获取栈顶部的两个元素 Object child = digester.peek(0); Object parent = digester.peek(1); if (digester.log.isDebugEnabled()) { if (parent == null) { digester.log.debug("[SetNextRule]{" + digester.match + "} Call [NULL PARENT]." + methodName + "(" + child + ")"); } else { digester.log.debug("[SetNextRule]{" + digester.match + "} Call " + parent.getClass().getName() + "." + methodName + "(" + child + ")"); } } // Call the specified method Class paramTypes[] = new Class[1]; if (paramType != null) { paramTypes[0] = digester.getClassLoader().loadClass(paramType); } else { paramTypes[0] = child.getClass(); } //调用parent类的方法(addSetNextRule中声明的方法,将)将child对象设置进去 if (useExactMatch) { MethodUtils.invokeExactMethod(parent, methodName, new Object[]{ child }, paramTypes); } else { MethodUtils.invokeMethod(parent, methodName, new Object[]{ child }, paramTypes); } }
注意:setNextRule是不做出栈操作的,出栈操作是objectCreate的end()函数进行的。(这里也体现了为什么倒序执行匹配规则的end()函数)
RulesBase类:
主要包含下面的方法:
public class RulesBase implements Rules {
protected HashMap cache = new HashMap();
protected ArrayList rules = new ArrayList();
protected Digester digester = null;
//...
public void setDigester(Digester digester) {
this.digester = digester;
Iterator items = rules.iterator();
while (items.hasNext()) {
Rule item = (Rule) items.next();
item.setDigester(digester);
}
}
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);
}
}
public List match(String pattern) {
return (match(null, pattern));
}
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);
}
}
从代码可以看出RulesBase将所有规则依据xml中的标签分类,将属于同一标签的规则按添加先后顺序保存在list中, 并以标签为key放在map中。
match()方法是将对应标签的list(规则)取出,而namespaceURI虽然看着繁琐,但其只是加了一层过滤,匹配出一些特定namespace的规则.