tomcat源碼解讀一 Digester的解析方式

1    Digester

Digester在tomcat中的作用是對conf下的server.xml文件進行實例化,其是從Catalian這個組件開始,創建Digester實例,再添加對應的規則,然後將其實例化,通過setServer方法,將其實例話的對象作爲當前Catalian實例的句柄。這樣就實現了對象句柄之間的關聯引用,從而實現整個平臺的遞進啓動。

 

1.1    UML類圖

1.2    UML時序圖

 

1.3    規則添加解析

1.3.1  添加對應解析規則

規則的添加實在Catalia.java的load()方法之中。規則主要是根據各個標籤創建對應對象的規則,以及解析對象的通過何種方法設爲相應句柄屬性。其主要實現過程是創建Digester實例,設置規則

 

protected Digester createStartDigester() {
    long t1=System.currentTimeMillis();
    //創建一個digester實例
    Digesterdigester = new Digester();
    //是否需要驗證xml文檔的合法性,false表示不需要進行DTD規則校驗
    digester.setValidating(false);
    //是否需要進行節點設置規則校驗
    digester.setRulesValidation(true);
    //將xml節點中的className作爲假屬性,不用調用默認的setter方法
    //在解析時,調用相應對象的setter方法來設置屬性值,setter的參數就是節點屬性,
    //而有className的話,則直接使用className來直接實例化對象
    HashMap<Class<?>,List<String>> fakeAttributes = new HashMap<>();
    ArrayList<String> attrs = new ArrayList<>();
    attrs.add("className");
    fakeAttributes.put(Object.class, attrs);
   digester.setFakeAttributes(fakeAttributes);
    digester.setUseContextClassLoader(true);

    //遇到xml中Server節點,就創建一個StandardServer對象注意在這裏只是添加了這個規則
    digester.addObjectCreate("Server",
                             "org.apache.catalina.core.StandardServer",
                             "className");

    //根據Server節點中的屬性信息,調用屬性的setter方法,比如說server節點中會有port=“8080”屬性,則會調用setPort方法
    digester.addSetProperties("Server");

    //在上面的load方法中有個digester.push(this),this對象就是棧頂了
    //這裏將Server節點對應的對象作爲參數,調用this對象,也就是Catalina對象的setServer方法
    //意思即將addObjectCreate 在解析後的對象通過this在digester.push(this)中通過setServer方法注入當前server對象
    //注意這裏只是添加規則
    digester.addSetNext("Server",
                        "setServer",
                        "org.apache.catalina.Server");

    //Server節點下的GlobalNamingResources節點,創建一個NamingResource對象
    digester.addObjectCreate("Server/GlobalNamingResources",
                             "org.apache.catalina.deploy.NamingResourcesImpl");
    digester.addSetProperties("Server/GlobalNamingResources");
    digester.addSetNext("Server/GlobalNamingResources",
                        "setGlobalNamingResources",
                        "org.apache.catalina.deploy.NamingResourcesImpl");

    //Server下的Listener節點
    digester.addObjectCreate("Server/Listener",
                             null, // MUST bespecified in the element
                             "className");
    digester.addSetProperties("Server/Listener");
    digester.addSetNext("Server/Listener",
                        "addLifecycleListener",
                        "org.apache.catalina.LifecycleListener");
    //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");

    //Service節點下的Listener節點
    digester.addObjectCreate("Server/Service/Listener",
                             null, // MUST bespecified 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");

    //給Connector添加規則,就是當遇到Connector的時候,會調用ConnectorCreateRule裏面定義的規則
    //跟上面的作用是一樣的,只不過該節點的規則比較多,就創建一個規則類
    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 bespecified in the element
                             "className");
    digester.addSetProperties("Server/Service/Connector/Listener");
    digester.addSetNext("Server/Service/Connector/Listener",
                        "addLifecycleListener",
                        "org.apache.catalina.LifecycleListener");

    // AddRuleSets 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/"));

    // Whenthe 'engine' is found, set the parentClassLoader.
    digester.addRule("Server/Service/Engine",
                     new SetParentClassLoaderRule(parentClassLoader));
    addClusterRuleSet(digester, "Server/Service/Engine/Cluster/");

    long t2=System.currentTimeMillis();
    if (log.isDebugEnabled()) {
        log.debug("Digester for server.xml created " + (t2-t1 ));
    }
    return (digester);
}


1.3.2  注入棧頂對象

將當前catalina壓入棧頂,stack 是一個ArrayStack實例

 

digester.push(this);

具體代碼如下:

       如果stack的大小爲0,則將當前對象賦值給root,這樣做的目的是在解析之後,能夠直接根據root句柄,返回當前對象

public void push(Object object) {

    if (stack.size() == 0) {
        root = object;
    }
    stack.push(object);

}
 
public Object parse(InputSource input) throws IOException,SAXException {
    configure();
    getXMLReader().parse(input);
    return (root);

}


1.3.3  解析標籤創建實例

在解析xml直接首先要獲取的xml閱讀器,在這裏獲取的是,其過程是通過getParser方法獲取對應的SAXParserImpl工廠,然後調用SAXParserImpl實例的newSAXParser方法,創建SAXParserImpl實例,然後設置相關屬性

public XMLReader getXMLReader() throws SAXException{
    if (reader == null) {
        reader =getParser().getXMLReader();
    }

    reader.setDTDHandler(this);
    reader.setContentHandler(this);

    if (entityResolver== null) {
        reader.setEntityResolver(this);
    } else {
        reader.setEntityResolver(entityResolver);
    }

    reader.setProperty("http://xml.org/sax/properties/lexical-handler", this);

    reader.setErrorHandler(this);
    return reader;
}


根據上述代碼,可以知道getXMLReader().parse(input);實際上調用的是SAXParserImpl中的 parse方法對input資源進行解析。方法如下:

public void parse(InputSource inputSource)
    throws SAXException,IOException {
    if (fSAXParser != null && fSAXParser.fSchemaValidator!= null) {
        if (fSAXParser.fSchemaValidationManager!= null) {
            fSAXParser.fSchemaValidationManager.reset();
            fSAXParser.fUnparsedEntityHandler.reset();
        }
        resetSchemaValidator();
    }
    super.parse(inputSource);
}


由上看出其繼續調用父類AbstractSAXParser的parse方法,在這個父類方法,其主要將資源文件轉化爲了XMLInputSource,設置其相關屬性,而後調用其重載方法,對XMLInputSource進行解析最終經過一系列轉化調用Digester的startDocument方法。這個方法主要是設置了一下編碼。在startDocument之後繼續開始掃描文檔,主要方法是scanDocument,開始對整個文檔開始進行解析,方法如下:

public boolean scanDocument(boolean complete)
throws IOException, XNIException {
    fEntityManager.setEntityHandler(this);

    int event =next();
    do {
        switch (event) {
            case XMLStreamConstants.START_DOCUMENT:
                break;
            case XMLStreamConstants.START_ELEMENT:
                break;
            case XMLStreamConstants.CHARACTERS :
               fDocumentHandler.characters(getCharacterData(),null);
                break;
            case XMLStreamConstants.SPACE:
                break;
            case XMLStreamConstants.ENTITY_REFERENCE:
                break;
            case XMLStreamConstants.PROCESSING_INSTRUCTION:
                fDocumentHandler.processingInstruction(getPITarget(),getPIData(),null);
                break;
            case XMLStreamConstants.COMMENT :
                fDocumentHandler.comment(getCharacterData(),null);
                break;
            case XMLStreamConstants.DTD :
                break;
            case XMLStreamConstants.CDATA:
                fDocumentHandler.startCDATA(null);
                //xxx: checkif CDATA values comes from getCharacterData() function
                fDocumentHandler.characters(getCharacterData(),null);
                fDocumentHandler.endCDATA(null);
                //System.out.println("in CDATA of the XMLNSDocumentScannerImpl");
                break;
            case XMLStreamConstants.NOTATION_DECLARATION:
                break;
            case XMLStreamConstants.ENTITY_DECLARATION:
                break;
            case XMLStreamConstants.NAMESPACE :
                break;
            case XMLStreamConstants.ATTRIBUTE :
                break;
            case XMLStreamConstants.END_ELEMENT:

                break;
            default :
                throw new InternalError("processingevent: " + event);

        }
        event = next();
    } while (event!=XMLStreamConstants.END_DOCUMENT&& complete);

    if(event ==XMLStreamConstants.END_DOCUMENT) {
        fDocumentHandler.endDocument(null);
        return false;
    }

    return true;

}


    開始調用startElement對元素開始解析,先拼接模式然後獲取其對應的規則,遍歷所有規則,調用其對應規則實例的begin方法,這要求所有規則實現抽象類Rule,規則的添加在上文解析過程中。

public void startElement(String namespaceURI, String localName, String qName, Attributes list)
        throws SAXException {



    list = updateAttributes(list);
    bodyTexts.push(bodyText);
    bodyText = new StringBuilder();
    String name = localName;
    if ((name == null) || (name.length() < 1)) {
        name = qName;
    }
    StringBuilder sb = new StringBuilder(match);
    if (match.length() > 0) {
        sb.append('/');
    }
    sb.append(name);
    match = sb.toString();
    if (debug) {
        log.debug("  New match='" + match + "'");
    }
    List<Rule> rules = getRules().match(namespaceURI, match);
    matches.push(rules);
    if ((rules != null) && (rules.size() > 0)) {
        for (int i = 0; i < rules.size(); i++) {
       
                Rule rule = rules.get(i);
                rule.begin(namespaceURI, name, list);
                 
    } 
}

  根據上文的解析規則與過程,下面介紹一些對哪些對象做了解析

 

1.3.4  Rule實現類begin方法

 

對應規則:

1.3.4.1  ObjectCreateRule的begin方法

 ===============================================================   

 

   創建對象是根據realClassName,根據類加載器創建其對應的實例,然後將這個實例給壓入digester的棧中,在這裏有必要解釋一下attributes這個屬性的集合來自於配置文件, getValue這個方法是根據attributeName==className來獲取對應的類名,這些值來自於server.xml中的解析,所以可以看出如果xml中存在,則優先使用xml中的值。只是默認server.xml中爲空

@Override
public void  begin(Stringnamespace, String name, Attributes attributes)
        throws Exception {
    String realClassName = className;
    if (attributeName!= null) {
        String value =attributes.getValue(attributeName);
        if (value != null) {
            realClassName = value;
        }
    }
……………………
    Class<?> clazz = digester.getClassLoader().loadClass(realClassName);
    Object instance =clazz.newInstance();
    digester.push(instance);
}


1.3.4.2  SetPropertiesRule

===============================================================     

 

   當前方法主要是對屬性進行規則驗證,如果需要進行規則驗證,且其是一個不合法的屬性,則輸出警告日誌。

public void begin(String namespace, String theName,Attributes attributes)
        throws Exception {

    // Populate thecorresponding properties of the top object
    Object top = digester.peek();

    for (int i = 0; i <attributes.getLength(); i++) {
        String name =attributes.getLocalName(i);
        if ("".equals(name)){
            name =attributes.getQName(i);
        }
        String value =attributes.getValue(i);
        if (!digester.isFakeAttribute(top,name)
                && !IntrospectionUtils.setProperty(top,name, value)
                && digester.getRulesValidation()){
            digester.log.warn("[SetPropertiesRule]{"+ digester.match +
                    "}Setting property '" + name + "' to '" +
                    value + "' didnot find a matching property.");
        }
    }

}


1.3.4.3  SetNextRule

===============================================================

  這個方法的begin什麼事情都沒有做

 

1.3.5  Rule實現類的body方法

這部分方法沒有進行任何處理

1.3.6  Rule實現類的end方法

=============================================================

SetNextRule:

SetNextRule[methodName=,paramType=org.apache.catalina.LifecycleListener]

       該方法是在標籤元素結束的時候調用,獲取當前對象以及其父級對象,然後根據方法名和參數類型調用調用父類方法,將當前實例注入作爲其句柄屬性。

public void end(String namespace, String name) throws Exception {
    Object child = digester.peek(0);
    Object parent = digester.peek(1);
    IntrospectionUtils.callMethod1(parent,methodName,
            child, paramType, digester.getClassLoader());

}
 


1.3.6.1  SetNextRule

===============================================================

  這個方法的end什麼事情都沒有做

 

1.3.6.2  ObjectCreateRule

==============================================================

 

  這個方法是將當前實例元素給移除棧頂

public void end(String namespace, String name) throws Exception {
    Object top = digester.pop();
}

 

1.3.7  標籤解析值

1.3.7.1  Server標籤

===============================================================

[className=org.apache.catalina.core.StandardServer,attributeName=className]

   其創建了一個StandardServer對象

 

   此時stack棧中的集合:

   Catalina@1590

   StandardServer@1788

 

===============================================================     

SetNextRule[methodName=setServer,paramType=org.apache.catalina.Server]

  默認實現方法中begin方法什麼也沒有做

 

===============================================================     

SetPropertiesRule[]

   驗證屬性是否符合規範並注入相應的值,在這裏給StandardServer注入了port=8005shutdown=SHUTDOWN

 

 

1.3.7.2  Server/Listener標籤

begin

===============================================================

ObjectCreateRule[className=null, attributeName=className]

   attributeName=>org.apache.catalina.startup.VersionLoggerListener

  從而創建對應實例然後壓入到stack棧:

  Catalina@1590

  StandardServer@1788

  VersionLoggerListener@1974

 

begin

===============================================================

SetPropertiesRule[]

   這裏並沒有什麼屬性設置到當前實例

 

begin

===============================================================

SetNextRule[methodName=addLifecycleListener,paramType=org.apache.catalina.LifecycleListener]

   默認實現方法中begin方法什麼也沒有做

 

end

===============================================================

SetNextRule[methodName=addLifecycleListener,paramType=org.apache.catalina.LifecycleListener]

 

              調用StandardServer@1788這個實例中的addLifecycleListener
(LifecycleListener lifecycleListener)方法,將VersionLoggerListener添加到其句柄lifecycle這個LifecycleSupport實例中去
 
end
===============================================================
SetPropertiesRule[]
這裏什麼也沒有做
 
end
===============================================================
ObjectCreateRule[className=null, attributeName=className]

   將棧頂元素從stack中移除,這裏移除的是VersionLoggerListener@1974

實例,所以此時棧頂元素

   Catalina@1590

   StandardServer@1788

 

相同原理依次加入

    AprLifecycleListener

    JreMemoryLeakPreventionListener 

 

   GlobalResourcesLifecycleListener

   ThreadLocalLeakPreventionListener

 

 

1.3.7.3  Server/Service標籤

begin

=============================================================

ObjectCreateRule[className=org.apache.catalina.core.StandardService,attributeName=className]

  創建StandardService實例壓入到棧中,此時棧中的元素:

  Catalina@1590

  StandardServer@1788

  StandardService

begin

============================================================

SetPropertiesRule[]
  設置屬性值,這裏將其name設置爲catalina
 
begin
===============================================================
SetNextRule[methodName=addService,paramType=org.apache.catalina.Service]
 
 
 

1.3.7.4  Server/Service/Connector標籤

1.3.7.5  Server/Service/Engine標籤

1.3.7.6  Server/Service/Realm標籤

1.3.7.7  Server/Service/Host標籤

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章