上文中介紹了Bootstrap是如何啓動Catalina的,現在來看看Catalina的作用:
1,Catalina通過Digester類加載server.xml,實例化server.xml中各個組件,併爲這些實例賦值(這個類是通過擴展SAX來完成的)。
2,調用server的start方法開啓server組件,server會一級一級的將start傳播下去,這樣各個組件就從這裏開啓了。
3,初始化命名空間(tomcat會使用JNDI技術,比如在server.xml中配置了數據庫連接池的話,就使用了JNDI)。最後還包裝了System.out和System.err。
這裏面的重點就是Digester解析server.xml的過程,先來看看start方法:
public void start() {
//這裏剔除了一些判斷,日誌,服務器鉤子函數,等代碼
......
if (getServer() == null) {
load();
}
......
getServer().start();
if (await) {
await();
stop();
}
}
根據第一個if語句可以知道server是通過load方法實例化的。load方法執行後,啓動服務器,邏輯很簡單。進入load方法:
public void load() {
//這個方法的篇幅過長,我踢掉一些對流程不重要的代碼
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
//獲取sever.xml配置文件
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);
}
}
//將sever.xml對應的流傳給digester,由digester解析它。
inputSource.setByteStream(inputStream);
//這個方法很重要,它將自己也就是Catalina這個對象放到了digester對象裏的一個棧裏面,後面解析xml實例化server後
//會從棧裏拿出Catalina對象調用它的setServer方法來設置Catalina.server屬性。server裏的service屬性也是通過這種形式
//設置的。
digester.push(this);
digester.parse(inputSource);
//server實例化後,將設置server.catalina屬性,這裏Catalina和Server是雙向關聯的。
getServer().setCatalina(this);
// 包裝了System.out和System.err
initStreams();
getServer().init();
}
如果想搞清楚出digester是如何加載server.xml並實例化各個組件的,你可能會進入 digester.parse(inputSource)方法,但這個方法
不能告訴你關鍵的內容。來看看digester是個什麼類:
public class Digester extends DefaultHandler2
DefaultHandler2是SAX擴展包下面的,不懂SAX的一定要先去了解下SAX,不然看Catalina的源碼很費勁。回過頭來看看load()方法中的
第一行代碼Digester digester = createStartDigester();進入createStartDigester方法:
/**
* Create and configure the Digester we will be using for startup.
*/
protected Digester createStartDigester() {
Digester digester = new Digester();
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("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");
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");
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor"}));
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
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/");
return (digester);
}
仔細看着這些參數,xml文檔的節點名稱都有與之對應的類比如Server節點對象org.apache.catalina.core.StandardServer
digester實例化Server時是實例化了它的子類StanderServer。
進入addObjectCreate方法:
public void addObjectCreate(String pattern, String className,
String attributeName) {
addRule(pattern,
new ObjectCreateRule(className, attributeName));
}
每調用一個add*方法都爲pattern映射了一個Rule的子類,addSetProperties,addSetNext都爲pattern映射了一個Rule的子類。
這個映射關係被存放在RulesBase.cache中:
public class RulesBase implements Rules {
protected HashMap<String,List<Rule>> cache =
new HashMap<String,List<Rule>>();
}
從這個數據結構可以知道每個節點的完整名稱對應一個Rule的list集合。來看看Rule是如何定義的:
public abstract class Rule {
public Digester getDigester() {
return (this.digester);
}
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
begin(attributes);
}
public void end(String namespace, String name)
throws Exception {
end();
}
}
begin和end是它的核心方法(有的子類可能沒有其中的某個方法,這裏爲了說明xml的解析流程所以說它們重要)。說到這裏是想說明:digster事先爲xml中的每個節點定義了多個規則。上面提到Degister繼承自DefaultHandler2,並重寫了它的startDocument(),startElement(),endDocument()endElement()等方法。Degister內部定義了一個XMLReader類型的成員變量reader,並將自己作爲ContentHandler和DTDHandler 傳給自己的成員變量reader。那麼reader在解析xml的時候就會回調Digster繼承自DefaultHandler2的startDocument(),startElement(),endDocument(),endElement()等回調方法。在回調方法中就會調用與當前節點對應的Rule類的回調方法。現在拿Server的初始化來舉例:在上面提到的createStartDigester()方法中爲Server添加了三個規則:
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
這三個方法內部會分別創建三個規則類,ObjectCreateRule,SetPropertiesRule,SetNextRule。這三個類的作用分別是創建指定類,將xml標籤上的屬性賦值給改類,將該類賦值給它的上級類。這三個類被創建後添加到了一個以“Server”爲鍵的map中前面提到的RulesBase.cache。在reader類解析xml到<Server>標籤的開始節點時會調用startElement()方法,並將當前節點的節點名和屬性值等一系列值傳給改方法。方法內部則通過節點全名稱獲取對應的規則類Rule對象的list結合,並挨個調用rule對象的begin方法。以server對應的ObjectCreateRule規則爲例:
public void begin(String namespace, String name, Attributes attributes)
throws Exception {
Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
Object instance = clazz.newInstance();
digester.push(instance);
}
創建了與server對應的實例(實例名是org.apache.catalina.core.StandardServer或是屬性classname對象的值)並放在了digest的一個名爲stack屬性的棧頂。前面的load方法中digester將當前類對象也就是Catalina對象push到了stack裏面,這時候Catalina應該在stack的底端,因爲之前stack裏沒有數據。server標籤是server.xml的第一個標籤,這時候解析到它的開始標籤並調用了與它對象的ObjectCreateRule規則的begin方法初始化了server對象並後push到了stack裏面,那麼此時stack有兩個元素,Catalina在棧底,server在棧頂。ObjectCreateRule的start方法結束後會繼續調用SetPropertiesRule的start方法,這個類是將標籤的屬性值賦值給上面創建的對象,它的begin方法的第一步就是從stack獲取對象,然後將標籤上的屬性值賦值給該對象比如<Server port="8005" shutdown="SHUTDOWN">中的port和shutdown屬性。最後是SetNextRule類,這個類只有一個end方法是在遇到</Server>的結束標籤時調用的。下面是該方法的源碼:
@Override
public void end(String namespace, String name) throws Exception {
Object child = digester.peek(0);
Object parent = digester.peek(1);
if (digester.log.isDebugEnabled()) {
if (parent == null) {
digester.log.debug("[SetNextRule]{" + digester.match +
"} Call [NULL PARENT]." +
methodName + "(" + child + ")");
} else {
digester.log.debug("[SetNextRule]{" + digester.match +
"} Call " + parent.getClass().getName() + "." +
methodName + "(" + child + ")");
}
}
IntrospectionUtils.callMethod1(parent, methodName,
child, paramType, digester.getClassLoader());
}
在調用這個方法前,degister的成員變量stack已經push和pop好幾個對象了,每次標籤開始解析時創建對象push到stack裏面,標籤結束後從stack裏pop出來。因爲<Server>標籤是頂層標籤,所以server對象最先被創建並push到stack裏面(Catalina一直在棧底),最後被pop出來,所以結束標籤</server>解析時和開始一樣,stack依然還是兩個元素,server在棧頂,Catalina在棧底。 Object child = digester.peek(0);Object
parent = digester.peek(1);分別取出了棧頂和棧底的元素。server.xml的解析不是很好敘述,它的關係有點複雜。要記住一點server.xml的每一級標籤對應一個tomcat組件,被外層標籤包裹的標籤是外層標籤的一個屬性比如serveice是server的一個屬性。xml在被解析時會根據當前的事件(開始,或結束)來調用對應節點的解析規則,創建對象,並通過棧來完成對象之間的關聯關係。