Tomcat是利用Digester解析server.xml。Digester是Apache下的开源项目
Digester官网:http://commons.apache.org/proper/commons-digester/
使用Digester,需要依赖一些jar包。Digester依赖的jar包可以从网上下载,也可以使用Tomcat提供的jar包
方案一:从网上下载jar包
commons-beanutils.jar
commons-digester.jar
commons-logging.jar
commons-collections.jar(下载commons-collections-3。commons-collections-4将commons-collections的package命名由org.apache.commons.collections改为org.apache.commons.collections4,而Digester内部仍按org.apache.commons.collections使用的)
方案二:使用Tomcat目录下的jar包
tomcat-juli.jar($CATALINA_HOME\bin)
tomcat-util.jar($CATALINA_HOME\lib)
tomcat-util-scan.jar($CATALINA_HOME\lib)
本文采用方案一
下面来看看Digester如何解析School.xml
<?xml version='1.0' encoding='utf-8'?>
<School name="CSDN">
<Grade name="1">
<Class name="1" number="31"/>
<Class name="2" number="32"/>
</Grade>
<Grade name="2">
<Class name="1" number="41"/>
<Class name="2" number="42"/>
</Grade>
</School>
School是School.xml的最顶层节点,School节点下包含Grade节点,Grade节点下包含Class节点。(学校下有两个年级,每个年级下有两个班级,班级下的number表示人数)
首先要创建与School.xml中各个节点对应的实体类School.java、Grade.java、Class.java(这些类都放在digester包下)
School.java
package digester;
public class School {
private String name;
private Grade grades[] = new Grade[0];
private final Object servicesLock = new Object();
public void addGrade(Grade g){
synchronized (servicesLock) {
Grade results[] = new Grade[grades.length + 1];
System.arraycopy(grades, 0, results, 0, grades.length);
results[grades.length] = g;
grades = results;
}
}
public Grade[] getGrades() {
return grades;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
School里的Grade数组用来存放School包含的Grade。调用School. addGrade(Grade g),可以往Grade数组添加Grade
Grade.java
package digester;
public class Grade {
private String name;
private Class classes[] = new Class[0];
private final Object servicesLock = new Object();
public void addClass(Class c){
synchronized (servicesLock) {
Class results[] = new Class[classes.length + 1];
System.arraycopy(classes, 0, results, 0, classes.length);
results[classes.length] = c;
classes = results;
}
}
public Class[] getClasses() {
return classes;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Class.java
package digester;
public class Class {
private String name;
private int number;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
然后创建测试类DigesterTest.java。DigesterTest.java的digester方法用来读取School.xml、创建Digester并设置规则、解析School.xml;print方法用来打印School的信息
package digester;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.tomcat.util.digester.Digester;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class DigesterTest {
private School school;
public School getSchool() {
return school;
}
public void setSchool(School s) {
this.school = s;
}
public void digester() throws IOException, SAXException{
//读取School.xml
File file = new File("D:\\School.xml");
InputStream inputStream = new FileInputStream(file);
InputSource inputSource = new InputSource(file.toURI().toURL().toString());;
inputSource.setByteStream(inputStream);
//创建Digester
Digester digester = new Digester();
//是否需要用DTD验证XML文档的合法性
digester.setValidating(false);
//将当前对象放到对象堆的最顶层
digester.push(this);
/* 下面开始为Digester创建匹配规则
* 在Digester内School、School/Grade、School/Grade/Class
* 分别对应School.xml的School、Grade、Class节点
*/
//为School创建规则
/*
* Digester.addObjectCreate(String pattern, String className, String attributeName)
* pattern--匹配的节点
* className--该节点对应的默认实体类
* attributeName--如果该节点有className属性,用className的值替换默认实体类
* Digester匹配到School节点,如果School节点没有className属性,将创建digester.School对象;如果School节点有className属性,将创建指定的(className属性的值)对象
*/
digester.addObjectCreate("School",
"digester.School",
"className");
//将指定节点的属性映射到对象,即将School节点的name的属性映射到School.java
digester.addSetProperties("School");
/*
* Digester.addSetNext(String pattern, String methodName, String paramType)
* pattern--匹配的节点
* methodName--调用父节点的方法
* paramType--父节点的方法接收的参数类型
* Digester匹配到School节点,将调用DigesterTest(School的父节点)的setSchool方法,参数为School对象
*/
digester.addSetNext("School",
"setSchool",
"digester.School");
//为School/Grade创建规则
digester.addObjectCreate("School/Grade",
"digester.Grade",
"className");
digester.addSetProperties("School/Grade");
//Grade的父节点为School
digester.addSetNext("School/Grade",
"addGrade",
"digester.Grade");
//为School/Grade/Class创建规则
digester.addObjectCreate("School/Grade/Class",
"digester.Class",
"className");
digester.addSetProperties("School/Grade/Class");
digester.addSetNext("School/Grade/Class",
"addClass",
"digester.Class");
digester.parse(inputSource);
}
//打印School信息
public void print(School s){
if(s!=null){
System.out.println(s.getName() + "有" + s.getGrades().length + "个年级");
for(int i=0;i<s.getGrades().length;i++){
if(s.getGrades()[i] !=null){
Grade g = s.getGrades()[i];
System.out.println(g.getName() + "年级 有 " + g.getClasses().length + "个班:");
for(int j=0;j<g.getClasses().length;j++){
if(g.getClasses()[j] !=null){
Class c = g.getClasses()[j];
System.out.println(c.getName() + "班有" + c.getNumber() + "人");
}
}
}
}
}
}
public static void main(String[] args) throws IOException, SAXException {
DigesterTest digesterTest = new DigesterTest();
digesterTest.digester();
digesterTest.print(digesterTest.school);
}
}
DigesterTest的输出:
CSDN有2个年级
1年级 有 2个班:
1班有31人
2班有32人
2年级 有 2个班:
1班有41人
2班有42人
Tomcat解析server.xml是在Catalina的load方法内进行的。load方法可以划分成三步:
1. 创建Digester并设置规则
load方法内通过Digester digester = createStartDigester()创建Digester 并设置规则。createStartDigester也是Catalina内的方法
createStartDigester方法的内容(为了简介,每种创建规则的方式各列一个)
protected Digester createStartDigester() {
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>();
ArrayList<String> attrs = new ArrayList<>();
attrs.add("className");
fakeAttributes.put(Object.class, attrs);
digester.setFakeAttributes(fakeAttributes);
digester.setUseContextClassLoader(true);
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
//父节点为Catalina
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
//父节点为Server
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
//为Connector节点创建规则
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
/*
* 如果某个节点包含的规则比较多,可以为该节点创建一个规则类
* 执行digester.addRuleSet(new EngineRuleSet("Server/Service/"))
* 可以将EngineRuleSet内包含的规则,添加到当前digester中
*/
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
//为Cluster节点创建规则
addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
}
2. 读取server.xml文件
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", file), e);
}
}
3. 利用Digester解析xml文件
try {
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
} catch (SAXParseException spe) {
log.warn("Catalina.start using " + getConfigFile() + ": " +
spe.getMessage());
return;
} catch (Exception e) {
log.warn("Catalina.start using " + getConfigFile() + ": " , e);
return;
} finally {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
参考文章:
http://blog.csdn.net/caihaijiang/article/details/5944955
http://www.cnblogs.com/bjzhanghao/archive/2005/03/25/125747.html