<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:來說說Tomcat的啟動過程是什么樣子的

          共 6413字,需瀏覽 13分鐘

           ·

          2021-01-16 17:39

          每天早上七點三十,準時推送干貨


          阿粉最近在瘋狂的研究各種用的工具里面的源碼實現(xiàn),之前給大家都專門的去扣了一下 JDK 里面自帶的exe程序,這次阿粉開始更加無聊,直接開始搞Tomcat。

          1.Tomcat分析

          阿粉知道作為一個 Java 資深開發(fā)人員,對 Tomcat 那是再熟悉不過了,bin目錄、conf目錄、webapps目錄,對這些目錄熟悉的簡直不能再熟悉了。一言不合就是一個shutdown.sh,或者來個shutdown.bat,但是你知道你的啟動startup.bat,和startup.sh他們的啟動過程是什么過程么?接下來我們就開始進入分析吧。

          2.Tomcat的整體結構圖

          這個整體結構圖可不是大家想的目錄結構圖,目錄結構圖阿粉就不給大家展示了,自己去打開你的 Tomcat,里面就有你想看到目錄結構圖,那么整體結構圖是什么樣子的呢?

          給大家解釋一下這個圖的意思,

          • Server:整個服務器。

          • Service:具體的服務。

          • Connector:提供Socket和request,response的連接。

          • Container:用于封裝和管理Servlet,以及具體的處理請求。

          這個圖就把里面的包含關系說的是明明白白了,為什么這么說呢?因為一個Server中可以存在多個服務,也就是多個Service,而一個Service中可以存在多個Connector,但是只能存在一個Container,是不是就非常的清晰了呢?

          3.Tomcat的啟動過程

          接下來我們就來看看源碼里面的啟動過程吧,Bootstrap類中的啟動過程。

          這個類的位置是在tomcat的catalina的包里面,大家看一下主方法也就是所謂的main方法,

          ??public?static?void?main(String[]?args)?{
          ????????//對象初始化
          ????????if?(daemon?==?null)?{
          ????????????Bootstrap?bootstrap?=?new?Bootstrap();

          ????????????try?{
          ????????????????bootstrap.init();
          ????????????}?catch?(Throwable?var3)?{
          ????????????????handleThrowable(var3);
          ????????????????var3.printStackTrace();
          ????????????????return;
          ????????????}

          ????????????daemon?=?bootstrap;
          ????????}?else?{
          ????????????Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
          ????????}

          ????????try?{
          ????????????String?command?=?"start";
          ????????????if?(args.length?>?0)?{
          ????????????????command?=?args[args.length?-?1];
          ????????????}

          ????????????if?(command.equals("startd"))?{
          ????????????????args[args.length?-?1]?=?"start";
          ????????????????//加載
          ????????????????daemon.load(args);
          ????????????????//啟動
          ????????????????daemon.start();
          ????????????}?else?if?(command.equals("stopd"))?{
          ????????????????args[args.length?-?1]?=?"stop";
          ????????????????//停止
          ????????????????daemon.stop();
          ????????????}?else?if?(command.equals("start"))?{
          ????????????????daemon.setAwait(true);
          ????????????????//加載并且啟動
          ????????????????daemon.load(args);
          ????????????????daemon.start();
          ????????????????if?(null?==?daemon.getServer())?{
          ????????????????????System.exit(1);
          ????????????????}
          ????????????}?else?if?(command.equals("stop"))?{
          ????????????????daemon.stopServer(args);
          ????????????}?else?if?(command.equals("configtest"))?{
          ????????????????daemon.load(args);
          ????????????????if?(null?==?daemon.getServer())?{
          ????????????????????System.exit(1);
          ????????????????}

          ????????????????System.exit(0);
          ????????????}?else?{
          ????????????????log.warn("Bootstrap:?command?\""?+?command?+?"\"?does?not?exist.");
          ????????????}
          ????????}?catch?(Throwable?var4)?{
          ????????????Throwable?t?=?var4;
          ????????????if?(var4?instanceof?InvocationTargetException?&&?var4.getCause()?!=?null)?{
          ????????????????t?=?var4.getCause();
          ????????????}

          ????????????handleThrowable(t);
          ????????????t.printStackTrace();
          ????????????System.exit(1);
          ????????}

          ????}

          main方法里面的存在也很簡單,先進行init的操作,然后再執(zhí)行start,也就是說,啟動過程中,首先要進行初始化,然后接下來再進行啟動,最后階段在來個stop,這樣才算完整嘛。

          • load方法:其實說白了load方法就是根據(jù)server.xml文件創(chuàng)建Server并且調用Server的init方法進行初始化。

          • start方法:start方法很直白,啟動服務器。

          • stop方法:stop方法同樣,停止服務器。

          在這里的start方法和stop方法調用的分別就是調用了Server內部的start和stop方法,而這三個方法都是按照圖中的層級結構來的,先從Server的load,start,stop,然后Server的start再調用Service的start,而Service的start調用的就是Connector和Container的start方法了,這從外到內的啟動,就可以把Tomcat完整的啟動起來了。

          我們在接下來就繼續(xù)從外到內的啟動開始分析一波。

          3.1 Catalina啟動過程

          上面的啟動入口我們已經成功找到了,那么我們就繼續(xù)來,對象初始化完成后,執(zhí)行了init的方法

          ??Bootstrap?bootstrap?=?new?Bootstrap();

          ????????????try?{
          ????????????????bootstrap.init();
          ????????????}?catch?(Throwable?var3)?{

          就是上面的這個,如果參數(shù)為空了,那么就開始調用start了,那么start方法是什么呢?

          ?public?void?start()?throws?Exception?{
          ????????if?(this.catalinaDaemon?==?null)?{
          ????????????this.init();
          ????????}

          ????????Method?method?=?this.catalinaDaemon.getClass().getMethod("start",?(Class[])null);
          ????????method.invoke(this.catalinaDaemon,?(Object[])null);
          ????}

          上面的start方法就是直接使用invoke的方法映射到了catalinaDaemon,也就是到了Catalina的start的方法上,

          而這個Catalina的啟動無非也就是調用了同樣的方法,setAwait方法,load方法,start方法,

          • setAwait方法:用于設置Server啟動完成時,是否進入等待,如果是true,那就等待,如果不是false,那就不進入等待。

          • load方法:創(chuàng)建并且初始化Server,

          • start方法:同樣是啟動服務器

          同樣的setAwait方法比較少,阿粉就不給大家看了,無非就是個判斷,而load方法一定得看,

          if?(!this.loaded)?{
          ????????????this.loaded?=?true;
          ????????????long?t1?=?System.nanoTime();
          ????????????????????try?{
          ????????????????????????inputSource.setByteStream((InputStream)inputStream);
          ????????????????????????digester.push(this);
          ????????????????????????digester.parse(inputSource);
          ????????????????????????break?label242;
          ????????????????????}?catch?(SAXParseException?var28)?{
          ????????????????????????log.warn("Catalina.start?using?"?+?this.getConfigFile()?+?":?"?+?var28.getMessage());
          ????????????????????????return;
          ????????????????????}?catch?(Exception?var29)?{
          ????????????????????????log.warn("Catalina.start?using?"?+?this.getConfigFile()?+?":?",?var29);
          ????????????????????}
          ????????????????}?finally?{
          ????????????????????if?(inputStream?!=?null)?{
          ????????????????????????try?{
          ????????????????????????????((InputStream)inputStream).close();
          ????????????????????????}?catch?(IOException?var23)?{
          ????????????????????????????;
          ????????????????????????}
          ????????????????????}

          ????????????????}

          ????????????????return;
          ????????????}
          ?try?{
          ????//此處同樣調用的Server的init方法,
          ????????????????this.getServer().init();
          ????????????}?catch?(LifecycleException?var24)?{
          ????????????????if?(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))?{
          ????????????????????throw?new?Error(var24);
          ????????????????}

          ????????????????log.error("Catalina.start",?var24);
          ????????????}

          ????????????long?t2?=?System.nanoTime();
          ????????????if?(log.isInfoEnabled())?{
          ????????????????log.info("Initialization?processed?in?"?+?(t2?-?t1)?/?1000000L?+?"?ms");
          ????????????}

          ????????}

          而從這里就開始進入下一步了,Server的啟動過程,因為從Catalina里面已經找到了getServer的初始化方法,接下來就是要進行Server的初始化,然后加載,然后啟動的過程了。

          3.2 Server的啟動過程

          Server是Tomcat里面的接口,而不是類,那么我們就只能去找實現(xiàn)它的子類來于是就找到了StandardServer extends LifecycleMBeanBase implements Server。

          阿粉一看有繼承,還有實現(xiàn),那就先看看LifecycleMBeanBase這個被繼承的類,于是再次去看了它,

          public?abstract?class?LifecycleMBeanBase?extends?LifecycleBase?implements?JmxEnabled?{

          嗯?還有繼承?繼續(xù)往下扒拉,

          public?abstract?class?LifecycleBase?implements?Lifecycle?{

          終于算是找到了,

          阿粉一看這init方法和start方法又調用了initInternal()和startInternal(),找來找去又回去了,而阿粉也從這里知道了,模板方法,是有自己的子類具體實現(xiàn)

          于是回到了StandardServer自己的init和start方法,

          ?protected?void?startInternal()?throws?LifecycleException?{
          ????????this.fireLifecycleEvent("configure_start",?(Object)null);
          ????????this.setState(LifecycleState.STARTING);
          ????????this.globalNamingResources.start();
          ????????Object?var1?=?this.servicesLock;
          ????????synchronized(this.servicesLock)?{
          ????????????for(int?i?=?0;?i?????????????????this.services[i].start();
          ????????????}

          ????????}
          ????}

          總得來說就是,StandardServer繼承自LifecycleMBeanBase,而LifecycleMBeanBase繼承自LifecycleBase,而LifecycleBase類中的模板方法,又讓自己的子類去進行具體的實現(xiàn),但是我們要知道他的Tomcat生命周期中存在這些內容才行。

          圖中都說了,Server里面有Service,那么一定就有,我們得去找找看,于是阿粉再次去找并且去看它到底是個什么意思,


          ????public?void?addService(Service?service)?{
          ????????service.setServer(this);
          ????????Object?var2?=?this.servicesLock;
          ????????synchronized(this.servicesLock)?{
          ????????????Service[]?results?=?new?Service[this.services.length?+?1];
          ????????????System.arraycopy(this.services,?0,?results,?0,?this.services.length);
          ????????????results[this.services.length]?=?service;
          ????????????this.services?=?results;
          ????????????if?(this.getState().isAvailable())?{
          ????????????????try?{
          ????????????????????service.start();
          ????????????????}?catch?(LifecycleException?var6)?{
          ????????????????????;
          ????????????????}
          ????????????}

          ????????????this.support.firePropertyChange("service",?(Object)null,?service);
          ????????}
          ????}

          位置是在Server的接口中出現(xiàn)了增加和刪除Service的方法,Server的init方法和start方法循環(huán)去調用每個Service的init方法和start方法。

          接下來我們看看Service的具體實現(xiàn),找到StandardService:

          ??protected?void?initInternal()?throws?LifecycleException?{
          ????????super.initInternal();
          ????????if?(this.engine?!=?null)?{
          ????????????this.engine.init();
          ????????}

          ????????Executor[]?arr$?=?this.findExecutors();
          ????????int?len$?=?arr$.length;

          ????????int?len$;
          ????????for(len$?=?0;?len$?????????????Executor?executor?=?arr$[len$];
          ????????????if?(executor?instanceof?JmxEnabled)?{
          ????????????????((JmxEnabled)executor).setDomain(this.getDomain());
          ????????????}

          ????????????executor.init();
          ????????}

          ????????this.mapperListener.init();
          ????????Object?var11?=?this.connectorsLock;
          ????????synchronized(this.connectorsLock)?{
          ????????????Connector[]?arr$?=?this.connectors;
          ????????????len$?=?arr$.length;

          ????????????for(int?i$?=?0;?i$?????????????????Connector?connector?=?arr$[i$];

          ????????????????try?{
          ????????????????????connector.init();
          ????????????????}?catch?(Exception?var9)?{
          ????????????????????String?message?=?sm.getString("standardService.connector.initFailed",?new?Object[]{connector});
          ????????????????????log.error(message,?var9);
          ????????????????????if?(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))?{
          ????????????????????????throw?new?LifecycleException(message);
          ????????????????????}
          ????????????????}
          ????????????}

          ????????}
          ????}

          而在方法中主要調用Executor,mapperListener,executor的init方法。

          connector之前已經有了,而這個mapperListener就是Mapper的監(jiān)聽器,用來堅挺container容器的變化。

          文獻參考

          《深入剖析Tomcat》?

          《Tomcat架構解析》?

          《Servlet/JSP深入詳解》


          PS公號內回復「Python」即可進入Python 新手學習交流群,一起?100 天計劃!


          老規(guī)矩,兄弟們還記得么,右下角的 “在看” 點一下,如果感覺文章內容不錯的話,記得分享朋友圈讓更多的人知道!

          神秘禮包獲取方式

          識別文末二維碼,回復:1024

          瀏覽 19
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一级a片黄色成人小视频 | 日韩8x8x | 成人午夜av | 多国五级毛片 | 国产男女操逼 |