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]