天天看點

tomcat源碼Catalina

Catalina的作用是初始化各個元件,并開始啟動各個元件。

上文中介紹了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在被解析時會根據目前的事件(開始,或結束)來調用對應節點的解析規則,建立對象,并通過棧來完成對象之間的關聯關系。

繼續閱讀