這篇文章要弄懂一個問題,我們知道,一個鏈接器是跟一個容器關聯的,容器跟鏈接器是在什麼時候關聯上的?
在明白這個問題前要先瞭解一下Digester庫,這個庫簡單的說就是解析xml文件,這裡有兩個概念:模式和規則,所謂模式就是一個xml的標簽,規則就是遇到一個xml標簽需要做什麼,看一下他主要的三個方法:
1:addObjectCreate(String pattern, String className, String attributeName) 根據模式pattern實例化一個對象className
2:addSetProperties(String pattern) 設置這個模式的屬性
3:addSetNext(String pattern, String methodName, String paramType) 添加模式之間的關系,調用父模式的
上面可能不好理解,看tomcat是怎麼用到Digester的,在org.apache.catalina.startup.Catalina.createStartDigester()的方法裡(這個方法是在服務組件啟動的時候調用的,詳細參考Tomcat源碼分析(一)–服務啟動),在這個方法裡有使用Digester來解析server.xml文件:
[java]
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");// 添加一個模式“Server/Service”,當在xml文件(這裡是server.xml)裡遇到“Server”標簽下的Service標簽的時候,根據標簽Service的屬性“className”為類名實例化一個對象,默認類名是"org.apache.catalina.core.StandardServer"
digester.addSetProperties("Server/Service"); //設置對象StandardService的屬性,有些什麼屬性由xml裡的Service標簽指定
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");//調用Service標簽的上一級標簽Server的對象(即StandardServer)的addService方法,把Service添加進Server,設置它們的關系,最後一個參數表示addService方法的參數類型
這樣StandardServer和StandardService便有瞭所屬關系,現在看容器跟鏈接器是怎麼連接的,再看createStartDigester方法:
[java]
digester.addObjectCreate("Server/Service/Connector",
"org.apache.catalina.connector.http.HttpConnector",
"className");
digester.addSetProperties("Server/Service/Connector");
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.Connector");
這裡很好理解,跟上面的是一樣的,遇到標簽Server/Service/Connector的時候(這裡簡化瞭說法,應該是標簽Server下的子標簽Service的子標簽Connector,有點拗口),實例化HttpConnector,然後在它的上一級父容器StandardService下調用addConnector,這樣就把鏈接器HttpConnector添加進容器StandardService下瞭,看StandardService的addConnector方法:
[java]
public void addConnector(Connector connector) {
synchronized (connectors) {
connector.setContainer(this.container); //本來這裡就應該把容器和連接器關聯上的,但是在一開始tomcat啟動的時候,Digester是先添加鏈接器,所以容器container還是為null的,但是沒有關系,後面還會另一個地方關聯他們,這裡應該牢記的是容器和連接器都是在Service裡面
connector.setService(this);
Connector results[] = new Connector[connectors.length + 1];
System.arraycopy(connectors, 0, results, 0, connectors.length);
results[connectors.length] = connector;
connectors = results;
if (initialized) {
try {
connector.initialize();
} catch (LifecycleException e) {
e.printStackTrace(System.err);
}
}
if (started && (connector instanceof Lifecycle)) {
try {
((Lifecycle) connector).start();
} catch (LifecycleException e) {
;
}
}
// Report this property change to interested listeners
support.firePropertyChange("connector", null, connector);
}
}
這個方法很簡單,就是把一個鏈接器connector添加到StandardService的connectors數組裡,然後關聯上StandardService的容器。代碼上也做瞭一點說明(很重要)。連接器添加進StandardService瞭,現在看容器是什麼時候添加進StandardService的,其實方法是一樣的,再回到createStartDigester方法:
[java]
digester.addRuleSet(new EngineRuleSet("Server/Service/"));//這句代碼是在createStartDigester方法裡面
————————–》下面進入EngineRuleSet類的addRuleInstances方法
public void addRuleInstances(Digester digester) {
digester.addObjectCreate(prefix + "Engine",
"org.apache.catalina.core.StandardEngine",
"className");
digester.addSetProperties(prefix + "Engine");
digester.addRule(prefix + "Engine",
new LifecycleListenerRule
(digester,
"org.apache.catalina.startup.EngineConfig",
"engineConfigClass"));
digester.addSetNext(prefix + "Engine",
"setContainer",
"org.apache.catalina.Container"); //這裡調用StandardService的方法setContainer方法,把容器添加進StandardService裡面
先不去糾結Digester是怎麼進入addRuleInstances方法的,當我們調用瞭digester.addRuleSet(new EngineRuleSet("Server/Service/"));方法,Digester便會自動調用到EngineRuleSet類的addRuleInstances方法,在方法裡面無非也是添加各種模式和規則,根據上面的添加規則,很容易知道這裡又添加瞭一個StandardEngine對象(容器),然後又在該模式的上一級模式Server/Service添加StandardEngine跟StandardService的關系,即通過setContainer方法把容器添加進StandardService裡。以下是StandardService的setContainer方法:
[java]
public void setContainer(Container container) {
Container oldContainer = this.container;
if ((oldContainer != null) && (oldContainer instanceof Engine))
((Engine) oldContainer).setService(null);
this.container = container;
if ((this.container != null) && (this.container instanceof Engine))
((Engine) this.container).setService(this);
if (started && (this.container != null) &&
(this.container instanceof Lifecycle)) {
try {
((Lifecycle) this.container).start();
} catch (LifecycleException e) {
;
}
} //重點!!!!!!!!
synchronized (connectors) { //下面是把StandardService下的所有連接器都關聯上StandardService下的容器,這樣連接器就跟容器關聯上瞭。
for (int i = 0; i < connectors.length; i++)
connectors[i].setContainer(this.container);
}
if (started && (oldContainer != null) &&
(oldContainer instanceof Lifecycle)) {
try {
((Lifecycle) oldContainer).stop();
} catch (LifecycleException e) {
;
}
}
// Report this property change to interested listeners
support.firePropertyChange("container", oldContainer, this.container);
}
上面的代碼做瞭各種判斷,然後把容器設置到StandardService下,在“同步代碼塊”處,把容器和鏈接器關聯上瞭,至此,容器和鏈接器就關聯上瞭。回過頭想想,其實很簡單,就是用Digester根據設定的模式讀取server.xml,然後調用瞭兩個關鍵的方法setContainer和addConnector,就把容器和鏈接器關聯上瞭。關聯上瞭就可以明白在Tomcat源碼分析(二)–連接處理一文的最後process方法裡代碼:connector.getContainer().invoke(request, response);的含義瞭。下篇希望說明白調用invoke之後發生的一切。
作者:haitao111313