上一篇文章我们了解了Digester的基本使用方法,今天这篇文章我们来看下Digester类在tomcat中的具体使用,tomcat使用Digester类来解析相关的xml文件,包括web.xml
和server.xml
,我们先讨论下server.xml
,web.xml
下次讨论。
在之前的tomcat启动过程源码讲解的时候,我们讲过Catalina
类实例的load()
,源码如下
/**
* Start a new server instance.
*/
public void load() {
long t1 = System.nanoTime();
initDirs();
// Before digester - it may be needed
initNaming();
//1111111111111111
// Create and execute our Digester
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
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);
}
}
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream(getConfigFile());
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(getConfigFile()).toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
getConfigFile()), e);
}
}
}
// This should be included in catalina.jar
// Alternative: don't bother with xml, just create it manually.
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream("server-embed.xml");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource("server-embed.xml").toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
"server-embed.xml"), e);
}
}
}
if (inputStream == null || inputSource == null) {
if (file == null) {
log.warn(sm.getString("catalina.configFail",
getConfigFile() + "] or [server-embed.xml]"));
} else {
log.warn(sm.getString("catalina.configFail",
file.getAbsolutePath()));
if (file.exists() && !file.canRead()) {
log.warn("Permissions incorrect, read permission is not allowed on the file.");
}
}
return;
}
try {
//222222222222222
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 {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
}
getServer().setCatalina(this);
// Stream redirection
initStreams();
// Start the new server
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if (log.isInfoEnabled()) {
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
源码在1处创建了一个Digester实例,在2处进行了解析,我们一步一步看,先查看创建处
/**
* Create and configure the Digester we will be using for startup.
*/
protected Digester createStartDigester() {
long t1 = System.currentTimeMillis();
// Initialize the digester
Digester digester = new Digester();
digester.setValidating(false);
digester.setRulesValidation(true);
HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<Class<?>, List<String>>();
ArrayList<String> attrs = new ArrayList<String>();
attrs.add("className");
fakeAttributes.put(Object.class, attrs);
digester.setFakeAttributes(fakeAttributes);
digester.setUseContextClassLoader(true);
// Configure the actions we will be using
//根据Server标签创建Server对象,实例是StandardServer,设置属性,并且调用前一个对象的setServer方法,将StandardServer对象设置进去,上一个对象是什么,等下再讲解
//1111111111
digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className");
digester.addSetProperties("Server");
digester.addSetNext("Server", "setServer", "org.apache.catalina.Server");
//创建NamingResources对象,并且设置到StandardServer中
digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResources");
//遇到Listener标签,根据标签上className属性上类创建对应的对象,设置对应Listener的属性并且将该对象设置到StandardServer中
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");
//遇到Service标签创建StandardService对象,然后设置其属性,然后将StandardService实例设置到StandardServer中
digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service", "addService", "org.apache.catalina.Service");
//遇到Service标签内部的Listener标签,跟上面一样创建对应的listener对象,然后设置到 StandardService对象中。
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");
//Executor
//创建对应的Executor 然后设置到StandardService中
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");
//22222222
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(new String[]{"executor"}));
//将Connecter实例添加到 StandardService中
digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector");
//创建Connector 内部的listener
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");
// Add RuleSets for nested elements
//33333333333
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/"));
//4444444444
// 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);
}
这段源码在了解Digester的基础上非常容易读懂,简单的代码我都写了一些注释,下面我们稍微关注下,标注1,2,3的地方。
标注1:
之所以查看这段代码是因为,只调用了一次addObjectCreate
就调用了addSetNext
,这个也许有读者看不懂,但是其实这里有个小细节,就是在上面一段源码(Catalina)标注2的地方,先调用了Digester对象的一个push
方法。大家都知道Digester对象实例内部有个stack专门用来存储临时对象,也就是解析的过程中被创建的对象,当解析结束的时候就会把这个对象pop
出去,所以在解析的一开始,Digester对象的堆栈中就已经存在了一个对象,那么这个对象就是Catalina的实例,所以第一个addSetNext
实际上是把第一个addObjectCreate
创建的对象set
到Catalina的实例里面去。
标注2
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(new String[]{"executor"}));
//将Connecter实例添加到 StandardService中
digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector");
我们在讲解tomcat启动的时候,讲到过Connector
的初始化,但是当时我们并不知道Connector
是如何初始化,而且我们只查看了他的构造方法,知道Connector
会根据协议的不同而去创建不同的对象。之所以需要关注代码2这里,是因为这里就隐藏了Connector
初始化的秘密。
addRule
这个方法我们之前讲解过,这个方法会给在Digester对象内部的规则对象(Rules对象,变量叫rules)中的规则列表(一个hashmap)中针对摸个匹配模式添加一条规则!所以我们直接看ConnectorCreateRule
对象即可。
class ConnectorCreateRule extends Rule
ConnectorCreateRule
继承了Rule
对象,复写了begin
方法,我们来查看具体源码
/**
* Process the beginning of this element.
*
* @param namespace the namespace URI of the matching element, or an
* empty string if the parser is not namespace aware or the element has
* no namespace
* @param name the local name if the parser is namespace aware, or just
* the element name otherwise
* @param attributes The attribute list for this element
*/
@Override
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
Service svc = (Service)digester.peek();
Executor ex = null;
if ( attributes.getValue("executor")!=null ) {
ex = svc.getExecutor(attributes.getValue("executor"));
}
Connector con = new Connector(attributes.getValue("protocol"));
if ( ex != null ) _setExecutor(con,ex);
digester.push(con);
}
参照createStartDigester
标注2之前的源码可以看到,digester内部stack里面栈顶的对象是StandardService
,所以peek()
返回的是StandardService
,如果Connector
标签上包含executor
属性,就根据executor
名字去StandardService
内部去获取一个Executor
,当然我们没有配置Executor
相关属性所以就直接看下面的了,下面就直接创建了一个Connector对象,传递的参数是Connector
标签上protocol
的值。我们查看server.xml
就知道protocol
会有两种值,HTTP/1.1
,AJP/1.3
,所以我们现在就找到了接收http请求的Connector
的实例的初始化部分就是在这里完成的,最后digester对象将新创建的Connector
实例又推到了栈顶,方便下次调用addSetNext
的时候将创建的Connector
实例设置到对应的StandardService
中。
注:peek()
返回但不取出栈顶第一个元素 pop()
返回并取出栈顶第一个元素。
标注3
//33333333333
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/"));
addRuleSet()
方法之前文章中也讲解过这个方法,需要的参数是是一个RuleSet
的实现类,我们看下EngineRuleSet
class EngineRuleSet extends RuleSetBase
继承了RuleSetBase
复写了addRuleInstances
方法。
prifix = "Server/Service/";
@Override
public void addRuleInstances(Digester digester) {
//解析到Engine标签以后,创建StandardEngine对象,设置属性,并且设置到StandardService对象中(根据之前的代码)
digester.addObjectCreate(prefix + "Engine", "org.apache.catalina.core.StandardEngine", "className");
digester.addSetProperties(prefix + "Engine");
digester.addRule(prefix + "Engine", new LifecycleListenerRule("org.apache.catalina.startup.EngineConfig", "engineConfigClass"));
digester.addSetNext(prefix + "Engine", "setContainer", "org.apache.catalina.Container");
//Cluster configuration start
digester.addObjectCreate(prefix + "Engine/Cluster", null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Engine/Cluster");
digester.addSetNext(prefix + "Engine/Cluster", "setCluster", "org.apache.catalina.Cluster");
//Cluster configuration end
//创建StandardEngine对象内部的Listener对象,并且设置属性,设置到Engine对象中
digester.addObjectCreate(prefix + "Engine/Listener", null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Engine/Listener");
digester.addSetNext(prefix + "Engine/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener");
digester.addRuleSet(new RealmRuleSet(prefix + "Engine/"));
//创建StandardEngine对象内部的Value对象,设置相关属性,设置到Engine内部
digester.addObjectCreate(prefix + "Engine/Valve", null, // MUST be specified in the element
"className");
digester.addSetProperties(prefix + "Engine/Valve");
digester.addSetNext(prefix + "Engine/Valve", "addValve", "org.apache.catalina.Valve");
}
这一长串的代码其实做的就是一件事情,那就是完善创建StandardEngine
对象并且设置内部各种属性和对象的引用。
如果你继续查看HostRuleSet
,ContextRuleSet
的源码你会发现其实都是在做相似的事情,所以我们就对标注3的代码比较明了了
标注3的代码给我们充分展示了tomcat内部比较重要的几个对象Engine,Host,Context他们是如何创建的,他们之间的引用关系是如何被设置的,可以看出Digester这个工具类解放了代码,可以使我们充分的利用xml
文件特有的灵活,帮我们解决了对象之间的依赖关系,而我们不需要写代码,只需要配置下xml
文件即可!
前面说到Digester在启动时候会解析xml
,其实在tomcat stop的时候也就是调用Catalina
的stop()
的时候也会创建一个Digeser对象,不过比较简单就留给读者自行理解了,当然tomcat不仅在解析server.xml
的时候使用了Digester,同时tomcat在解析web.xml
的时候也使用了Digester,这个有空我们可以再写一篇文章解析一下!