Tomcat源碼分析(三)–連接器是如何與容器關聯的? – JAVA編程語言程序開發技術文章

這篇文章要弄懂一個問題,我們知道,一個鏈接器是跟一個容器關聯的,容器跟鏈接器是在什麼時候關聯上的?
  在明白這個問題前要先瞭解一下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

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *