Digester庫
在前面的幾個章節裡,我們對tomcat裡各個元件的配置完全是使用寫寫死的形式完成的。
如
Context context = new StandardContext();
Loader loader = new WebappLoader();
context.setLoader(loader);
就完成了向context容器裡添加WepappLoader的功能。
這麼做的問題就在于,一旦我想更改配置就必須得重新加載Bootstrap類。
幸運的是tomcat的設計者使用了一種給我靈活的配置方式,即使用xml來記錄tomcat裡各個元件的配置情況。并且使用Digester将xml中的元素轉化為java對象。
Digester是Apache軟體基金會Jakarta項目下的一個開源項目。更多具體的資訊請問百度。
這裡我們主要介紹Digester能幹什麼。
在apaceh官網上對這個項目的描述如下
XML-to-Java-object mapping utility.
看到了吧,就是咱們在上面說的把xml裡面的元素轉化為java類。
Digester類
先看一個xml 如下
<?xml version="1.0" encoding="ISO-8859-1"?>
<employee firstName="Freddie" lastName="Mercury">
<office description="Headquarters">
<address streetName="Wellington Avenue" streetNumber="223"/>
</office>
</employee>
在這個xml裡面,根元素是employee,裡面包含一個元素office,office裡面又包含一個元素address。
在讨論Digester如何解析xml前,我們先說兩個概念,模式與規則。
模式:我無法給出一個書面的關于模式的定義。粗略的說在上面的xml中,employee元素的模式是employee;office元素的模式是employee/office;以此類推address元素的模式就是employee/office/address。大家應該大概明白了吧。說白了模式就是路徑。
規則:是org.apache.commons.digester.Rule類的執行個體。規則指定了在分析xml時遇到某一規則應該執行的動作。說的夠清楚了吧。另外Rule還有begin與end方法。擋在解析到比對某個模式的元素的開始标簽時會指向begin方法,end還用說嗎?
例如在解析上面的xml時:
1 先解析到了employee元素,此時查找是否有對應的規則與此模式即employee比對,若有,執行Rule對象的begin方法
2 遇到了office元素,此時查找是否有對應的規則與此模式即employee/office比對,若有,執行Rule對象的begin方法
3 遇到了address元素的開始标簽,此時查找是否有對應的規則與此模式即employee/office/address比對,若有,執行Rule對象的begin方法
4 遇到address元素的結束标簽,調用想比對規則對象的end方法。
5 6自己補全,我不寫了。
Digester預定義的規則
Digester預定義的規則主要有以下幾個。
建立對象
建立對象一共有四種方式
最主要的兩個例子如下:
digester.addObjectCreate("employee",ex15.pyrmont.digestertest.Employee.class);
或者
digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee");
第一個參數是模式名,第二個參數可以是類名(String類型),也可以使Class。
還有兩種方式就是在xml中寫明要加載的類。
若xml中如下
<employee firstName="Brian" lastName="May"
className="ex15.pyrmont.digestertest.Employee">
代碼寫成下面的樣子
digester.addObjectCreate("employee","ex15.pyrmont.digestertest.Employee", "className");
employee是類名,程式會按照上面方法的第三個參數去employee元素裡找對應的屬性值作為要加載的類,當然如果找不到那個屬性的話,就是有上面方法的第二個參數來加載。同樣第二個參數可以使String類型的類名也可以使Class。
(employee,office,address都在文章的最後)
設定屬性
digester.addSetProperties("employee");
上面這句代碼做的事情就是:系統找到比對employee模式的元素後,如果那個元素有屬性,就将相應的屬性值注入到類裡面去。
<employee firstName="Brian" lastName="May">
如果xml中如上,裡面在它所比對的那個類裡面就至少得有setFirstName,setLastName兩個方法。
調用方法
digester.addCallMethod("employee", "printName");
上面代碼的功能就是找到比對employee模式的元素後(準确的說是遇到結束标簽後),就調用最先建立的那個類的printName方法。
建立對象之間的聯系
digester.addSetNext("employee/office", "addOffice");
第一個參數的模式應該是這種形式的
firstobject/secondobjcet
上面的代碼的意思就是調用firstobject的addOffice方法,并且以secondobject為參數。當然這兩個object前面都已經産生好了。
下面這個例子讓大家熟悉一下Digester的用法
示例代碼1
package ex15.pyrmont.digestertest;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.commons.digester.Digester;
public class Test02 {
public static void main(String[] args) {
String path = System.getProperty("user.dir") + File.separator +
"src"+File.separator + "etc";
File file = new File(path, "employee2.xml");
Digester digester = new Digester();
// add rules
digester.addObjectCreate("employee", "ex15.pyrmont.digestertest.Employee");
digester.addSetProperties("employee");
digester.addCallMethod("employee", "printName");
digester.addObjectCreate("employee/office", "ex15.pyrmont.digestertest.Office");
digester.addSetProperties("employee/office");
digester.addSetNext("employee/office", "addOffice");
digester.addObjectCreate("employee/office/address", "ex15.pyrmont.digestertest.Address");
digester.addSetProperties("employee/office/address");
digester.addSetNext("employee/office/address", "setAddress");
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();
}
}
}
employee2.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>
至于最後的運作結果大家自己猜一下,然後運作一下看看。
Rule類
rule,顧名思義就是規則,就是當解析器解析到某個模式時要幹什麼事。
可能大家要問,不是已經有前面的addObjectCreate了麼?問題是如果我想讓程式每解析出一個"類"就做個什麼事怎麼辦?建立一個類隻是各種規則的一種而已。
org.apache.commons.digester.rule裡面有兩個方法比較重要,分别是begin與end。
public void begin(Attributes attributes) throws Exception
public void end() throws Exception
當在解析到某種模式的是時候,遇到開始标簽就調用begin方法,結束标簽就調用end方法。
現在我們說說之前的addObjectCreate方法。
Digester.java
protected Rules rules = null;
public void addObjectCreate(String pattern, Class clazz) {
addRule(pattern,new ObjectCreateRule(clazz));
}
public void addRule(String pattern, Rule rule) {
rule.setDigester(this);
getRules().add(pattern, rule); //getRules傳回的就是Digester中的那個rules
}
ObjectCreateRule.java
public void begin(Attributes attributes) throws Exception {
....
// Instantiate the new object and push it on the context stack
Class clazz = digester.getClassLoader().loadClass(realClassName);
Object instance = clazz.newInstance();
digester.push(instance);
}
Digester裡面存儲了一個rules對象
rules是一個接口 我們使用的是它的實作類RulesBase
在RulesBase中有下面的代碼
protected ArrayList rules = new ArrayList();
另外ObjectCreateRule也是rule的子類。
換言之,包括addObjectCreate在内前面說的幾個預定義規則幹的事情就是在Digester的rules裡面添加一個rule而已。
等到掃描器掃描到指定的模式時,就執行指定的rule裡的begin,end。
RuleSet
這是什麼東西?
大家看代碼就懂了。
package ex15.pyrmont.digestertest;
import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import org.apache.commons.digester.Digester;
public class Test03 {
public static void main(String[] args) {
String path = System.getProperty("user.dir") + File.separator +
"src"+File.separator + "etc";
File file = new File(path, "employee2.xml");
Digester digester = new Digester();
digester.addRuleSet(new EmployeeRuleSet()); //EmployeeRuleSet是什麼東西?
try {
Employee employee = (Employee) digester.parse(file);
...... 與Test2一樣
}
catch(Exception e) {
e.printStackTrace();
}
}
}
package ex15.pyrmont.digestertest;
import org.apache.commons.digester.Digester;
import org.apache.commons.digester.RuleSetBase;
//一定要繼承RuleSetBase
public class EmployeeRuleSet extends RuleSetBase {
//複寫 addRuleInstances
public void addRuleInstances(Digester digester) {
// add rules
digester.addObjectCreate("employee", "ex15.pyrmont.digestertest.Employee");
digester.addSetProperties("employee");
digester.addObjectCreate("employee/office", "ex15.pyrmont.digestertest.Office");
digester.addSetProperties("employee/office");
digester.addSetNext("employee/office", "addOffice");
digester.addObjectCreate("employee/office/address","ex15.pyrmont.digestertest.Address");
digester.addSetProperties("employee/office/address");
digester.addSetNext("employee/office/address", "setAddress");
}
}
RuleSetBase就是規則的集合嘛。
Test03與Test02的結果一樣,但是test3的代碼看起來更少一些,就是因為我們把規則都隐藏在了EmployeeRuleSet裡面了。
相關代碼
package ex15.pyrmont.digestertest;
import java.util.ArrayList;
public class Employee {
private String firstName;
private String lastName;
private ArrayList<Office> offices = new ArrayList<Office>();
public Employee() {
System.out.println("Creating Employee");
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
System.out.println("Setting firstName : " + firstName);
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
System.out.println("Setting lastName : " + lastName);
this.lastName = lastName;
}
public void addOffice(Office office) {
System.out.println("Adding Office to this employee");
offices.add(office);
}
public ArrayList<Office> getOffices() {
return offices;
}
public void printName() {
System.out.println("My name is " + firstName + " " + lastName+"sssssssss");
}
}
package ex15.pyrmont.digestertest;
public class Office {
private Address address;
private String description;
public Office() {
System.out.println("..Creating Office");
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
System.out.println("..Setting office description : " + description);
this.description = description;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
System.out.println("..Setting office address : " + address);
this.address = address;
}
}
package ex15.pyrmont.digestertest;
public class Address {
private String streetName;
private String streetNumber;
public Address() {
System.out.println("....Creating Address");
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
System.out.println("....Setting streetName : " + streetName);
this.streetName = streetName;
}
public String getStreetNumber() {
return streetNumber;
}
public void setStreetNumber(String streetNumber) {
System.out.println("....Setting streetNumber : " + streetNumber);
this.streetNumber = streetNumber;
}
public String toString() {
return "...." + streetNumber + " " + streetName;
}
}