1. 介绍
digester最初是struct的一个工具模块,功能是解析xml,后来分离出来形成commons-digester工具类库。
digester底层是基于SAX、事件驱动、栈的方式来搭建实现的:
- SAX,用于解析xml
- 事件驱动,在SAX解析的过程中加入事件来支持我们的对象映射
- 栈,在解析xml元素的开始和结束时,需要通过xml元素映射的类对象的入栈、出栈来完成事件的调用
下面给个简单示例及解释:
<dependency>
<groupId>commons-digester</groupId>
<artifactId>commons-digester</artifactId>
<version>2.1</version>
</dependency>
public class Foo {
private List<Bar> bars = new ArrayList<>();
public void addBar(Bar bar) {
bars.add(bar);
}
@Override
public String toString() {
return "Foo{" +
"bars=" + bars +
'}';
}
}
public class Bar {
int id;
String title;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "Bar{" +
"id=" + id +
", title='" + title + '\'' +
'}';
}
}
<foo name="the Parent">
<bar id="123" title="the first Child"></bar>
<bar id="456" title="the second Child"></bar>
</foo>
注意:下面调用的digester各个方法,只是解释XML的层级节点关系,跟每层相同节点的个数没有关系,如上面xml中,bar有两个,但下面规则中只需要设置1次。
public class DigestTest {
public static void main(String[] args) throws IOException, SAXException {
File file = new File("D:/foo.xml");
InputStream inputStream = new FileInputStream(file);
InputSource inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.setByteStream(inputStream);
Digester digester = new Digester();
// 不进行XML与相应的DTD合法性验证
digester.setValidating(false);
// 当遇到<foo>时创建一个digester.Foo对象,并将其放在栈顶
digester.addObjectCreate("foo", "digester.Foo");
// 根据<foo>元素的属性,name="the Parent",对刚创建的Foo对象属性进行设置
digester.addSetProperties("foo");
// 当遇到子元素<bar>时,创建一个digester.Bar对象,并将其放在栈顶
digester.addObjectCreate("foo/bar", "digester.Bar");
// 根据<bar>元素的属性,id="123" title="the first child",对刚创建的Bar对象属性进行设置
digester.addSetProperties("foo/bar");
// 对这个bar节点,调用其父节点foo的addBar方法,该方法参数类型为digeseter.Bar
digester.addSetNext("foo/bar", "addBar", "digester.Bar");
Foo foo = (Foo) digester.parse(inputSource);
System.out.println(foo);
}
}
运行结果为:
重点介绍下几个方法:
-
Digester.addObjectCreate(String pattern, String className, String attributeName)
pattern:匹配的节点
className:该节点对应的默认实体类
attributeName:如果该节点有className属性, 用className的值替换默认实体类
如:
<foo className = "digest.Foo"></foo>
Digester.addObjectCreate("foo", Foo1.class.getName, "className");
上面在xml中foo节点className是digester.Foo,会创建digester.Foo的实例替换Foo1.class.getName的实例;如果xml中foo节点没有className属性,会创建Foo1.class.getName的实例 -
digester.addSetProperties(“XXX”);
将指定节点的属性映射到对象中。
如:
<bar id="123" title="the Child"></bar>
digester.addSetProperties("foo/bar");
上面会将xml中bar节点的id、title属性映射到创建的Bar对象实例的id、name属性中。 -
Digester.addSetNext(String pattern, String methodName, String paramType)
pattern:匹配的节点
methodName:调用父节点的方法
paramType:父节点的方法接收的参数类型
如:
<foo name="the Parent">
<bar id="123" title="the Child"></bar>
</foo>
digester.addSetNext("foo/bar", "addBar", "digester.Bar");
匹配到xml中bar节点,调用其父节点foo的方法addBar,这个方法的参数类型是digester.Bar。
public void addRule(String pattern, Rule rule) // 当匹配到pattern模式时,增加一个自定义规则
public void addRuleSet(RuleSet ruleSet) // 增加规则集,一个规则集指的是对一个节点及下面的所有后续节点(子节点、子节点的子节点...)的解析
2. Tomcat中server.xml解析
下面再看Tomcat中server.xml的解析,就很容易了。
Catalina.createStartDigester():
protected Digester createStartDigester() {
long t1 = System.currentTimeMillis();
// Initialize the digester
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
// 设置无效属性
Map<Class<?>, List<String>> fakeAttributes = new HashMap<>();
List<String> objectAttrs = new ArrayList<>();
objectAttrs.add("className");
fakeAttributes.put(Object.class, objectAttrs);
// Ignore attribute added by Eclipse for its internal tracking
List<String> contextAttrs = new ArrayList<>();
contextAttrs.add("source");
fakeAttributes.put(StandardContext.class, contextAttrs);
digester.setFakeAttributes(fakeAttributes);
// 设置是否使用线程上下文类加载器
digester.setUseContextClassLoader(true);
// Configure the actions we will be using
// Server
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
// Server/GlobalNamingResources
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResourcesImpl");
// Server/Listener
digester.addObjectCreate("Server/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
// Server/Service
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
// Server/Service/Listener
digester.addObjectCreate("Server/Service/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Listener");
digester.addSetNext("Server/Service/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
// Server/Service/Executor
digester.addObjectCreate("Server/Service/Executor",
"org.apache.catalina.core.StandardThreadExecutor",
"className");
digester.addSetProperties("Server/Service/Executor");
digester.addSetNext("Server/Service/Executor",
"addExecutor",
"org.apache.catalina.Executor");
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor", "sslImplementationName"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
digester.addObjectCreate("Server/Service/Connector/SSLHostConfig",
"org.apache.tomcat.util.net.SSLHostConfig");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig");
digester.addSetNext("Server/Service/Connector/SSLHostConfig",
"addSslHostConfig",
"org.apache.tomcat.util.net.SSLHostConfig");
digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
new CertificateCreateRule());
digester.addRule("Server/Service/Connector/SSLHostConfig/Certificate",
new SetAllPropertiesRule(new String[]{"type"}));
digester.addSetNext("Server/Service/Connector/SSLHostConfig/Certificate",
"addCertificate",
"org.apache.tomcat.util.net.SSLHostConfigCertificate");
digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
"org.apache.tomcat.util.net.openssl.OpenSSLConf");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf");
digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf",
"setOpenSslConf",
"org.apache.tomcat.util.net.openssl.OpenSSLConf");
digester.addObjectCreate("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
"org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
digester.addSetProperties("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd");
digester.addSetNext("Server/Service/Connector/SSLHostConfig/OpenSSLConf/OpenSSLConfCmd",
"addCmd",
"org.apache.tomcat.util.net.openssl.OpenSSLConfCmd");
digester.addObjectCreate("Server/Service/Connector/Listener",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/Listener");
digester.addSetNext("Server/Service/Connector/Listener",
"addLifecycleListener",
"org.apache.catalina.LifecycleListener");
digester.addObjectCreate("Server/Service/Connector/UpgradeProtocol",
null, // MUST be specified in the element
"className");
digester.addSetProperties("Server/Service/Connector/UpgradeProtocol");
digester.addSetNext("Server/Service/Connector/UpgradeProtocol",
"addUpgradeProtocol",
"org.apache.coyote.UpgradeProtocol");
// Add RuleSets for nested elements
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/");
digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));
// When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(parentClassLoader));
addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");
long t2 = System.currentTimeMillis();
if (log.isDebugEnabled()) {
log.debug("Digester for server.xml created " + (t2 - t1));
}
return digester;
}