<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>

          總在說 Spring Boot 內(nèi)置了 Tomcat 啟動,那它的原理你說的清楚嗎?

          共 12113字,需瀏覽 25分鐘

           ·

          2020-09-14 22:26

          程序員的成長之路
          互聯(lián)網(wǎng)/程序員/技術/資料共享?
          關注


          閱讀本文大概需要 7 分鐘。

          來自:cnblogs.com/sword-successful/p/11383723.html

          前言
          不得不說SpringBoot的開發(fā)者是在為大眾程序猿謀福利,把大家都慣成了懶漢,xml不配置了,連tomcat也懶的配置了,典型的一鍵啟動系統(tǒng),那么tomcat在springboot是怎么啟動的呢?

          內(nèi)置tomcat

          開發(fā)階段對我們來說使用內(nèi)置的tomcat是非常夠用了,當然也可以使用jetty。

          <dependency>
          ???<groupId>org.springframework.bootgroupId>
          ???<artifactId>spring-boot-starter-webartifactId>
          ???<version>2.1.6.RELEASEversion>
          dependency>
          @SpringBootApplication
          public?class?MySpringbootTomcatStarter{
          ????public?static?void?main(String[]?args)?{
          ????????Long?time=System.currentTimeMillis();
          ????????SpringApplication.run(MySpringbootTomcatStarter.class);
          ??????? System.out.println("===應用啟動耗時:"+(System.currentTimeMillis()-time)+"===");
          ????}
          }

          這里是main函數(shù)入口,兩句代碼最耀眼,分別是SpringBootApplication注解和SpringApplication.run()方法。

          發(fā)布生產(chǎn)

          發(fā)布的時候,目前大多數(shù)的做法還是排除內(nèi)置的tomcat,打瓦包(war)然后部署在生產(chǎn)的tomcat中,好吧,那打包的時候應該怎么處理?


          ????org.springframework.boot
          ????spring-boot-starter-web
          ????
          ????
          ????????
          ????????????org.springframework.boot
          ????????????spring-boot-starter-tomcat
          ????????

          ????




          ????javax.servlet
          ????javax.servlet-api
          ????3.1.0
          ????provided

          更新main函數(shù),主要是繼承SpringBootServletInitializer,并重寫configure()方法。
          @SpringBootApplication
          public?class?MySpringbootTomcatStarter?extends?SpringBootServletInitializer?{
          ????public?static?void?main(String[]?args)?{
          ????????Long?time=System.currentTimeMillis();
          ????????SpringApplication.run(MySpringbootTomcatStarter.class);
          ????????System.out.println("===應用啟動耗時:"+(System.currentTimeMillis()-time)+"===");
          ????}

          ????@Override
          ????protected?SpringApplicationBuilder?configure(SpringApplicationBuilder?builder)?{
          ????????return?builder.sources(this.getClass());
          ????}
          }

          從main函數(shù)說起

          public?static?ConfigurableApplicationContext?run(Class?primarySource,?String...?args)?{
          ????return?run(new?Class[]{primarySource},?args);
          }

          --這里run方法返回的是ConfigurableApplicationContext
          public?static?ConfigurableApplicationContext?run(Class[]?primarySources,?String[]?args)?
          {
          ?return?(new?SpringApplication(primarySources)).run(args);
          }
          public?ConfigurableApplicationContext?run(String...?args)?{
          ?ConfigurableApplicationContext?context?=?null;
          ?Collection?exceptionReporters?=?new?ArrayList();
          ?this.configureHeadlessProperty();
          ?SpringApplicationRunListeners?listeners?=?this.getRunListeners(args);
          ?listeners.starting();

          ?Collection?exceptionReporters;
          ?try?{
          ??ApplicationArguments?applicationArguments?=?new?DefaultApplicationArguments(args);
          ??ConfigurableEnvironment?environment?=?this.prepareEnvironment(listeners,?applicationArguments);
          ??this.configureIgnoreBeanInfo(environment);

          ??//打印banner,這里你可以自己涂鴉一下,換成自己項目的logo
          ??Banner?printedBanner?=?this.printBanner(environment);

          ??//創(chuàng)建應用上下文
          ??context?=?this.createApplicationContext();
          ??exceptionReporters?=?this.getSpringFactoriesInstances(SpringBootExceptionReporter.class,?new?Class[]{ConfigurableApplicationContext.class},?context);

          ??//預處理上下文
          ??this.prepareContext(context,?environment,?listeners,?applicationArguments,?printedBanner);

          ??//刷新上下文
          ??this.refreshContext(context);

          ??//再刷新上下文
          ??this.afterRefresh(context,?applicationArguments);

          ??listeners.started(context);
          ??this.callRunners(context,?applicationArguments);
          ?}?catch?(Throwable?var10)?{

          ?}

          ?try?{
          ??listeners.running(context);
          ??return?context;
          ?}?catch?(Throwable?var9)?{

          ?}
          }

          既然我們想知道tomcat在SpringBoot中是怎么啟動的,那么run方法中,重點關注創(chuàng)建應用上下文(createApplicationContext)和刷新上下文(refreshContext)。

          創(chuàng)建上下文

          //創(chuàng)建上下文
          protected?ConfigurableApplicationContext?createApplicationContext()?{
          ?Class?contextClass?=?this.applicationContextClass;
          ?if?(contextClass?==?null)?{
          ??try?{
          ???switch(this.webApplicationType)?{
          ????case?SERVLET:
          ????????????????????//創(chuàng)建AnnotationConfigServletWebServerApplicationContext
          ????????contextClass?=?Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
          ?????break;
          ????case?REACTIVE:
          ?????contextClass?=?Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
          ?????break;
          ????default:
          ?????contextClass?=?Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
          ???}
          ??}?catch?(ClassNotFoundException?var3)?{
          ???throw?new?IllegalStateException("Unable?create?a?default?ApplicationContext,?please?specify?an?ApplicationContextClass",?var3);
          ??}
          ?}

          ?return?(ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
          }

          這里會創(chuàng)建AnnotationConfigServletWebServerApplicationContext類。而AnnotationConfigServletWebServerApplicationContext類繼承了ServletWebServerApplicationContext,而這個類是最終集成了AbstractApplicationContext。

          刷新上下文

          //SpringApplication.java
          //刷新上下文
          private?void?refreshContext(ConfigurableApplicationContext?context)?{
          ?this.refresh(context);
          ?if?(this.registerShutdownHook)?{
          ??try?{
          ???context.registerShutdownHook();
          ??}?catch?(AccessControlException?var3)?{
          ??}
          ?}
          }

          //這里直接調(diào)用最終父類AbstractApplicationContext.refresh()方法
          protected?void?refresh(ApplicationContext?applicationContext)?{
          ?((AbstractApplicationContext)applicationContext).refresh();
          }
          //AbstractApplicationContext.java
          public?void?refresh()?throws?BeansException,?IllegalStateException?{
          ?synchronized(this.startupShutdownMonitor)?{
          ??this.prepareRefresh();
          ??ConfigurableListableBeanFactory?beanFactory?=?this.obtainFreshBeanFactory();
          ??this.prepareBeanFactory(beanFactory);

          ??try?{
          ???this.postProcessBeanFactory(beanFactory);
          ???this.invokeBeanFactoryPostProcessors(beanFactory);
          ???this.registerBeanPostProcessors(beanFactory);
          ???this.initMessageSource();
          ???this.initApplicationEventMulticaster();
          ???//調(diào)用各個子類的onRefresh()方法,也就說這里要回到子類:ServletWebServerApplicationContext,調(diào)用該類的onRefresh()方法
          ???this.onRefresh();
          ???this.registerListeners();
          ???this.finishBeanFactoryInitialization(beanFactory);
          ???this.finishRefresh();
          ??}?catch?(BeansException?var9)?{
          ???this.destroyBeans();
          ???this.cancelRefresh(var9);
          ???throw?var9;
          ??}?finally?{
          ???this.resetCommonCaches();
          ??}

          ?}
          }
          //ServletWebServerApplicationContext.java
          //在這個方法里看到了熟悉的面孔,this.createWebServer,神秘的面紗就要揭開了。
          protected?void?onRefresh()?{
          ?super.onRefresh();
          ?try?{
          ??this.createWebServer();
          ?}?catch?(Throwable?var2)?{

          ?}
          }

          //ServletWebServerApplicationContext.java
          //這里是創(chuàng)建webServer,但是還沒有啟動tomcat,這里是通過ServletWebServerFactory創(chuàng)建,那么接著看下ServletWebServerFactory
          private?void?createWebServer()?{
          ?WebServer?webServer?=?this.webServer;
          ?ServletContext?servletContext?=?this.getServletContext();
          ?if?(webServer?==?null?&&?servletContext?==?null)?{
          ??ServletWebServerFactory?factory?=?this.getWebServerFactory();
          ??this.webServer?=?factory.getWebServer(new?ServletContextInitializer[]{this.getSelfInitializer()});
          ?}?else?if?(servletContext?!=?null)?{
          ??try?{
          ???this.getSelfInitializer().onStartup(servletContext);
          ??}?catch?(ServletException?var4)?{

          ??}
          ?}

          ?this.initPropertySources();
          }

          //接口
          public?interface?ServletWebServerFactory?{
          ????WebServer?getWebServer(ServletContextInitializer...?initializers);
          }

          //實現(xiàn)
          AbstractServletWebServerFactory
          JettyServletWebServerFactory
          TomcatServletWebServerFactory
          UndertowServletWebServerFactory

          這里ServletWebServerFactory接口有4個實現(xiàn)類
          而其中我們常用的有兩個:TomcatServletWebServerFactory和JettyServletWebServerFactory。

          //TomcatServletWebServerFactory.java
          //這里我們使用的tomcat,所以我們查看TomcatServletWebServerFactory。到這里總算是看到了tomcat的蹤跡。
          @Override
          public?WebServer?getWebServer(ServletContextInitializer...?initializers)?{
          ?Tomcat?tomcat?=?new?Tomcat();
          ?File?baseDir?=?(this.baseDirectory?!=?null)???this.baseDirectory?:?createTempDir("tomcat");
          ?tomcat.setBaseDir(baseDir.getAbsolutePath());
          ????//創(chuàng)建Connector對象
          ?Connector?connector?=?new?Connector(this.protocol);
          ?tomcat.getService().addConnector(connector);
          ?customizeConnector(connector);
          ?tomcat.setConnector(connector);
          ?tomcat.getHost().setAutoDeploy(false);
          ?configureEngine(tomcat.getEngine());
          ?for?(Connector?additionalConnector?:?this.additionalTomcatConnectors)?{
          ??tomcat.getService().addConnector(additionalConnector);
          ?}
          ?prepareContext(tomcat.getHost(),?initializers);
          ?return?getTomcatWebServer(tomcat);
          }

          protected?TomcatWebServer?getTomcatWebServer(Tomcat?tomcat)?{
          ?return?new?TomcatWebServer(tomcat,?getPort()?>=?0);
          }

          //Tomcat.java
          //返回Engine容器,看到這里,如果熟悉tomcat源碼的話,對engine不會感到陌生。
          public?Engine?getEngine()?{
          ????Service?service?=?getServer().findServices()[0];
          ????if?(service.getContainer()?!=?null)?{
          ????????return?service.getContainer();
          ????}
          ????Engine?engine?=?new?StandardEngine();
          ????engine.setName(?"Tomcat"?);
          ????engine.setDefaultHost(hostname);
          ????engine.setRealm(createDefaultRealm());
          ????service.setContainer(engine);
          ????return?engine;
          }
          //Engine是最高級別容器,Host是Engine的子容器,Context是Host的子容器,Wrapper是Context的子容器

          getWebServer這個方法創(chuàng)建了Tomcat對象,并且做了兩件重要的事情:把Connector對象添加到tomcat中,configureEngine(tomcat.getEngine());
          getWebServer方法返回的是TomcatWebServer。

          //TomcatWebServer.java
          //這里調(diào)用構造函數(shù)實例化TomcatWebServer
          public?TomcatWebServer(Tomcat?tomcat,?boolean?autoStart)?{
          ?Assert.notNull(tomcat,?"Tomcat?Server?must?not?be?null");
          ?this.tomcat?=?tomcat;
          ?this.autoStart?=?autoStart;
          ?initialize();
          }

          private?void?initialize()?throws?WebServerException?{
          ????//在控制臺會看到這句日志
          ?logger.info("Tomcat?initialized?with?port(s):?"?+?getPortsDescription(false));
          ?synchronized?(this.monitor)?{
          ??try?{
          ???addInstanceIdToEngineName();

          ???Context?context?=?findContext();
          ???context.addLifecycleListener((event)?->?{
          ????if?(context.equals(event.getSource())?&&?Lifecycle.START_EVENT.equals(event.getType()))?{
          ?????removeServiceConnectors();
          ????}
          ???});

          ???//===啟動tomcat服務===
          ???this.tomcat.start();

          ???rethrowDeferredStartupExceptions();

          ???try?{
          ????ContextBindings.bindClassLoader(context,?context.getNamingToken(),?getClass().getClassLoader());
          ???}
          ???catch?(NamingException?ex)?{

          ???}

          ????????????//開啟阻塞非守護進程
          ???startDaemonAwaitThread();
          ??}
          ??catch?(Exception?ex)?{
          ???stopSilently();
          ???destroySilently();
          ???throw?new?WebServerException("Unable?to?start?embedded?Tomcat",?ex);
          ??}
          ?}
          }
          //Tomcat.java
          public?void?start()?throws?LifecycleException?{
          ?getServer();
          ?server.start();
          }
          //這里server.start又會回到TomcatWebServer的
          public?void?stop()?throws?LifecycleException?{
          ?getServer();
          ?server.stop();
          }
          //TomcatWebServer.java
          //啟動tomcat服務
          @Override
          public?void?start()?throws?WebServerException?{
          ?synchronized?(this.monitor)?{
          ??if?(this.started)?{
          ???return;
          ??}
          ??try?{
          ???addPreviouslyRemovedConnectors();
          ???Connector?connector?=?this.tomcat.getConnector();
          ???if?(connector?!=?null?&&?this.autoStart)?{
          ????performDeferredLoadOnStartup();
          ???}
          ???checkThatConnectorsHaveStarted();
          ???this.started?=?true;
          ???//在控制臺打印這句日志,如果在yml設置了上下文,這里會打印
          ???logger.info("Tomcat?started?on?port(s):?"?+?getPortsDescription(true)?+?"?with?context?path?'"
          ?????+?getContextPath()?+?"'");
          ??}
          ??catch?(ConnectorStartFailedException?ex)?{
          ???stopSilently();
          ???throw?ex;
          ??}
          ??catch?(Exception?ex)?{
          ???throw?new?WebServerException("Unable?to?start?embedded?Tomcat?server",?ex);
          ??}
          ??finally?{
          ???Context?context?=?findContext();
          ???ContextBindings.unbindClassLoader(context,?context.getNamingToken(),?getClass().getClassLoader());
          ??}
          ?}
          }

          //關閉tomcat服務
          @Override
          public?void?stop()?throws?WebServerException?{
          ?synchronized?(this.monitor)?{
          ??boolean?wasStarted?=?this.started;
          ??try?{
          ???this.started?=?false;
          ???try?{
          ????stopTomcat();
          ????this.tomcat.destroy();
          ???}
          ???catch?(LifecycleException?ex)?{

          ???}
          ??}
          ??catch?(Exception?ex)?{
          ???throw?new?WebServerException("Unable?to?stop?embedded?Tomcat",?ex);
          ??}
          ??finally?{
          ???if?(wasStarted)?{
          ????containerCounter.decrementAndGet();
          ???}
          ??}
          ?}
          }

          附:tomcat頂層結構圖

          tomcat最頂層容器是Server,代表著整個服務器,一個Server包含多個Service。從上圖可以看除Service主要包括多個Connector和一個Container。Connector用來處理連接相關的事情,并提供Socket到Request和Response相關轉化。
          Container用于封裝和管理Servlet,以及處理具體的Request請求。那么上文提到的Engine>Host>Context>Wrapper容器又是怎么回事呢?我們來看下圖:
          綜上所述,一個tomcat只包含一個Server,一個Server可以包含多個Service,一個Service只有一個Container,但有多個Connector,這樣一個服務可以處理多個連接。
          多個Connector和一個Container就形成了一個Service,有了Service就可以對外提供服務了,但是Service要提供服務又必須提供一個宿主環(huán)境,那就非Server莫屬了,所以整個tomcat的聲明周期都由Server控制。

          總結

          SpringBoot的啟動主要是通過實例化SpringApplication來啟動的,啟動過程主要做了以下幾件事情:配置屬性、獲取監(jiān)聽器,發(fā)布應用開始啟動事件初、始化輸入?yún)?shù)、配置環(huán)境,輸出banner、創(chuàng)建上下文、預處理上下文、刷新上下文、再刷新上下文、發(fā)布應用已經(jīng)啟動事件、發(fā)布應用啟動完成事件。
          在SpringBoot中啟動tomcat的工作在刷新上下這一步。而tomcat的啟動主要是實例化兩個組件:Connector、Container,一個tomcat實例就是一個Server,一個Server包含多個Service,也就是多個應用程序,每個Service包含多個Connector和一個Container,而一個Container下又包含多個子容器。

          推薦閱讀:

          Docker 被禁?別慌,問題不大!還有這些替代品!

          SpringBoot+RabbitMQ (保證消息100%投遞成功并被消費)

          5T技術資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復「2048」,即可免費獲?。?!

          微信掃描二維碼,關注我的公眾號

          朕已閱?

          瀏覽 54
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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片久久| 8050午| 日本黄色高清视频 | 久操国产精品 | 超碰网站在线看 |