Tomcat源碼學(xué)習(xí)第2篇 - Bootstrap的初始化加載
在上一篇中我們說(shuō)到Tomcat的啟動(dòng)入口是Bootstrap,那么今天我們就來(lái)研究一下,Bootstrap類在啟動(dòng)main方法之后是如何進(jìn)行各項(xiàng)初始化來(lái)提供后續(xù)程序的運(yùn)行的。
1. Bootstrap.main()方法
在main方法中我們可以看到,首先是創(chuàng)建了一個(gè)Bootstrap對(duì)象,并行初始化了一些相關(guān)的類加載器等操作,然后將創(chuàng)建的對(duì)象賦值給了 daemon,然后通過(guò)運(yùn)行時(shí)附帶的參數(shù)來(lái)選擇它后續(xù)對(duì)應(yīng)的操作,這里我們直接看到 start 這里。(具體參數(shù)攜帶可以查看Tomcat項(xiàng)目的bin/startup.sh文件)

public?static?void?main(String?args[])?{
?????//?實(shí)例化一個(gè)當(dāng)前Bootstrap引導(dǎo)類對(duì)象,并進(jìn)行類加載器的初始化,然后把該對(duì)象賦值給?daemon
????????synchronized?(daemonLock)?{
?????????...????
????????}
????????try?{
????????????String?command?=?"start";
????????????if?(args.length?>?0)?{
????????????????command?=?args[args.length?-?1];
????????????}
???//?執(zhí)行命令分支判斷
????????????if?(command.equals("startd"))?{
????????????????...
????????????}?else?if?(command.equals("stopd"))?{
????????????????...
????????????}?else?if?(command.equals("start"))?{???//?啟動(dòng)?tomcat?時(shí)傳入的是?start?命令參數(shù),走此分支
????????????????daemon.setAwait(true);
????????????????//?加載初始化
????????????????daemon.load(args);
????????????????//?啟動(dòng)
????????????????daemon.start();
????????????????if?(null?==?daemon.getServer())?{
????????????????????System.exit(1);
????????????????}
????????????}?else?if?(command.equals("stop"))?{
????????????????...
????????????}?else?if?(command.equals("configtest"))?{
????????????????...
????????????}?else?{
????????????????log.warn("Bootstrap:?command?\""?+?command?+?"\"?does?not?exist.");
????????????}
????????}?catch?(Throwable?t)?{
????????????...
????????}
????}
2. Bootstrap.load()方法
進(jìn)入load方法之后,我們可以看到它內(nèi)部實(shí)際是通過(guò)反射來(lái)調(diào)用 Catalina.load方法來(lái)進(jìn)行的初始化加載。
3. Catalina.load()方法
在這個(gè)方法中,他會(huì)創(chuàng)建一個(gè)用于解析 XML 文件的對(duì)象 digester,然后通過(guò) configFile方法得到conf/server.xml文件,并對(duì)其進(jìn)行解析,得到server對(duì)象(通過(guò)查看conf/server.xml文件,我們可以看到整個(gè)配置文件中,最外層的標(biāo)簽就是server,其內(nèi)部再一層層的嵌套其余的標(biāo)簽)。
public?void?load()?{
????//?用于解析XML配置文件
????Digester?digester?=?createStartDigester();
????InputSource?inputSource?=?null;
????InputStream?inputStream?=?null;
????File?file?=?null;
????try?{
????????try?{
????????????//?定位到配置文件?server.xml
????????????file?=?configFile();
????????????inputStream?=?new?FileInputStream(file);
????????????inputSource?=?new?InputSource(file.toURI().toURL().toString());
????????}?catch?(Exception?e)?{
????????????...
????????}
????????try?{
????????????inputSource.setByteStream(inputStream);
????????????digester.push(this);
????????????//?調(diào)用?digester?對(duì)象真正的去解析?xml
????????????digester.parse(inputSource);
????????}?catch?(Exception?e)?{
????????????...
????????}
????????
????????try?{
????????????//?執(zhí)行server.init
????????????getServer().init();
????????}?catch?(LifecycleException?e)?{
????????????...
????????}
????}?finally?{
????????...
????}
}


拿到server對(duì)象之后,調(diào)用了一個(gè) init方法,點(diǎn)擊該方法跳轉(zhuǎn)到對(duì)應(yīng)實(shí)現(xiàn)中

4. LifecycleBase.initInternal()方法
LifecycleBase是Tomcat中組件生命周期的統(tǒng)一管理接口的實(shí)現(xiàn)類,該類對(duì)相關(guān)組件的生命周期進(jìn)行了統(tǒng)一管理,通過(guò)剛剛的跳轉(zhuǎn),我們找到了LifecycleBase.initInternal方法,該方法為抽象方法,需要再跳轉(zhuǎn)到對(duì)應(yīng)的子類中,這是一種典型設(shè)計(jì)模式《模板模式》,在父類中定義好了整體的步驟,具體的實(shí)現(xiàn)由子類自己去實(shí)現(xiàn)。
5. StandardServer.initInternal()方法
DEBUG打上斷點(diǎn),再按F7進(jìn)入方法內(nèi)部,來(lái)到StandardServer.initInternal處。
這方法中有兩處地方調(diào)用了初始化方法:
globalNamingResources.init() 和 service.init(),分別對(duì)應(yīng)著配置文件中的兩處子標(biāo)簽,我們將重點(diǎn)放在service的初始化操作中,Tomcat中的連接器Connector與容器Engine都在這里。
Tomcat中支持多個(gè)service的配置,所以此處需要遍歷進(jìn)行初始化操作。

6. StandardService.initInternal()方法
通過(guò)service.init方法,我們又回到了生命周期基礎(chǔ)類中,重新調(diào)用initInternal方法進(jìn)入對(duì)應(yīng)的StandardService類中,在該類的initInternal方法中我們可以看到有engine和connector的初始化操作。

7. StandardEngine.initInternal()方法
繼續(xù)套娃,進(jìn)入StandardEngine查看具體實(shí)現(xiàn)。在這里得到對(duì)應(yīng)的Realm,然后返回。

8. Connector.initInternal()方法
繼續(xù)往下走,來(lái)到connector.init()這里,初始化連接器組件。繼續(xù)往下走,看到了有針對(duì)protocolHandler.init()的一個(gè)初始化操作。


9. AbstractProtocol.init()方法
先對(duì)endpoint對(duì)象進(jìn)行簡(jiǎn)單的設(shè)置,然后再對(duì)其進(jìn)行初始化操作。
10. AbstractEndpoint.init()方法
這里主要是做一個(gè)端口的綁定,具體的實(shí)現(xiàn)由NioEndpoint來(lái)進(jìn)行實(shí)現(xiàn)
11. NioEndpoint.bind()方法
在該方法中我們可以看到他創(chuàng)建了一個(gè)socket通道,綁定了我們?cè)谂渲梦募性O(shè)置的IP地址與端口。
總結(jié)
到此Bootstrap.load()方法中的初始化操作基本就完結(jié)了,我們?cè)谂渲梦募性O(shè)置的各個(gè)組件的參數(shù)均已進(jìn)行初始化。
完整組件線路如下:
Server
?Service
??Connector
?? EndPoint:通信端點(diǎn)(TCP/IP)
?? Processor:報(bào)文解析(HTTP/AJP)
?? Adapter:轉(zhuǎn)換器
??Container
???Catalina
??? Engine:引擎,是Servlet容器Catalina的核心,它支持在其下定義多個(gè)虛擬主機(jī)
??? Host:虛擬主機(jī),允許Tomcat引擎在將配置在一臺(tái)機(jī)器上的多個(gè)域名分割開來(lái)互相不干擾
??? Context:上下文對(duì)象
??? Wrapper:包裝組件
俄羅斯套娃:initInternal()
- END -我收集有眾多的 計(jì)算機(jī)電子書籍,有需要的小伙伴自提哦~
