上回说到,Catalina的load方法,主要是创建Digester对象来解析conf/server.xml,以此来创建Server、Service等相关组件类。
关键步骤:
Digester digester = createStartDigester();
digester.push(this);
digester.parse(inputSource);
1.建立解析规则
2.将Catalina对象压栈
3.开始解析server.xml
先看看server.xml的结构(为方便阅读,删了些注释)
<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JasperListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
</Host>
</Engine>
</Service>
</Server>
再对比Catalina.createStartDigester();
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.setClassLoader(StandardServer.class.getClassLoader());
// Configure the actions we will be using
//解析conf/server.xml,当碰到Server节点时将创建StandardServer对象,并压栈。
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
//对Server节点的属性(如port、shutdown)将调用StandardServer的setter方法进行赋值
digester.addSetProperties("Server");
//碰到第二个Server节点,即</Server>,将弹出栈顶的StandardServer对象,
//并作为新栈顶Catalina对象的setServer方法的参数,对其赋值
//Catalina对象来自digester.push(this)
//此时Catalina对象将持有Server对象
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
digester.addObjectCreate("Server/GlobalNamingResources",
"org.apache.catalina.deploy.NamingResources");
digester.addSetProperties("Server/GlobalNamingResources");
digester.addSetNext("Server/GlobalNamingResources",
"setGlobalNamingResources",
"org.apache.catalina.deploy.NamingResources");
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");
//StandardServer对象、StandardService对象互相引用,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");
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
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");
//前面的addObjectCreate也是调用addRule来实现规则的保存
//具体创建什么样类型的Connector,跟Connector节点的protocol的值有关系
//见ConnectorCreateRule.begin();创建Connector对象:
//Connector con = new Connector(attributes.getValue("protocol"));
//server.xml中默认有两种类型HTTP/1.1、AJP/1.3
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor"}));
//Connector实例出栈时,作为Service.addConnector的参数
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
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
//RuleBaseSet的继承类都要实现addRuleInstances方法,方法中一般保存解析规则,
//在digester.addRuleSet();调用此方法
digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/"));
/*将Engine实例赋值给StatandService对象的container*/
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
/*
* StandardHost实例赋添加到StandardEngine实例的children中
* 并给StandardHost实例添加监听器
* */
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
/*
* StandardContext实例赋添加到StandardHost实例的children中
* 并给StandardContext实例添加监听器
* */
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("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));
digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Cluster/"));
long t2=System.currentTimeMillis();
if (log.isDebugEnabled())
log.debug("Digester for server.xml created " + ( t2-t1 ));
return (digester);
}
主要的类:Catalina持有 Server对象,Server对象持有Service对象(可能多个),Service对象持有Connector对象和Engine对象,Engine对象持有Host对象,Host对象持有Context对象。网上找了张图,比较直观点(http://www.360doc.com/content/10/0910/14/3200988_52628292.shtml):
这些对象间关系的建立,具体细节还需要再打开像addRuleSet这样方法才能探个究竟。
http://127.0.0.1:8080/test/main.jsp --> http://host:port/context/page.jsp
一个tomcat上要部署两个项目,并且host、port、context都不一样,那么必须配两组<service>节点
如果一个项目需要支持多个host、port、context只要一组<service>节点,然后配多组host、connector、context即可。