上文中介紹了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() { //這里剔除了一些推斷,日志,server鉤子函數,等代碼 ...... if (getServer() == null) { load(); } ...... getServer().start(); if (await) { await(); stop(); } }
依據第一個if語句能夠知道server是通過load方法實例化的。load方法運行后,啟動服務器,邏輯非常easy。進入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在被解析時會依據當前的事件(開始,或結束)來調用相應節點的解析規則,創建對象,并通過棧來完畢對象之間的關聯關系。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
