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

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

          共 12161字,需瀏覽 25分鐘

           ·

          2020-08-10 20:34

          回復(fù)“1024”獲取?2000+?道互聯(lián)網(wǎng)大廠面試題


          作者:歪頭兒在帝都

          cnblogs.com/sword-successful/p/11383723.html

          前言

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

          內(nèi)置tomcat

          開發(fā)階段對我們來說使用內(nèi)置的tomcat是非常夠用了,當(dāng)然也可以使用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("===應(yīng)用啟動耗時(shí):"+(System.currentTimeMillis()-time)+"===");
          ????}
          }

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

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

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

          <dependency>
          ????<groupId>org.springframework.bootgroupId>
          ????<artifactId>spring-boot-starter-webartifactId>
          ????
          ????<exclusions>
          ????????<exclusion>
          ????????????<groupId>org.springframework.bootgroupId>
          ????????????<artifactId>spring-boot-starter-tomcatartifactId>
          ????????exclusion>
          ????exclusions>
          dependency>

          <dependency>
          ????<groupId>javax.servletgroupId>
          ????<artifactId>javax.servlet-apiartifactId>
          ????<version>3.1.0version>
          ????<scope>providedscope>
          dependency>

          更新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("===應(yīng)用啟動耗時(shí):"+(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,這里你可以自己涂鴉一下,換成自己項(xiàng)目的logo
          ??Banner?printedBanner?=?this.printBanner(environment);
          ??
          ??//創(chuàng)建應(yīng)用上下文
          ??context?=?this.createApplicationContext();
          ??exceptionReporters?=?this.getSpringFactoriesInstances(SpringBootExceptionReporter.class,?new?Class[]{ConfigurableApplicationContext.class},?context);

          ??//預(yù)處理上下文
          ??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方法中,重點(diǎn)關(guān)注創(chuàng)建應(yī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,而這個(gè)類是最終集成了AbstractApplicationContext。Java知音公眾號內(nèi)回復(fù)“后端面試”,送你一份Java面試題寶典

          刷新上下文

          //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)用各個(gè)子類的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
          //在這個(gè)方法里看到了熟悉的面孔,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);
          }

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

          這里ServletWebServerFactory接口有4個(gè)實(shí)現(xiàn)類

          而其中我們常用的有兩個(gè):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這個(gè)方法創(chuàng)建了Tomcat對象,并且做了兩件重要的事情:把Connector對象添加到tomcat中,configureEngine(tomcat.getEngine());

          getWebServer方法返回的是TomcatWebServer。

          //TomcatWebServer.java
          //這里調(diào)用構(gòu)造函數(shù)實(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服務(wù)===
          ???this.tomcat.start();

          ???rethrowDeferredStartupExceptions();

          ???try?{
          ????ContextBindings.bindClassLoader(context,?context.getNamingToken(),?getClass().getClassLoader());
          ???}
          ???catch?(NamingException?ex)?{
          ????????????????
          ???}
          ????????????
          ????????????//開啟阻塞非守護(hù)進(jìn)程
          ???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服務(wù)
          @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設(shè)置了上下文,這里會打印
          ???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());
          ??}
          ?}
          }

          //關(guān)閉tomcat服務(wù)
          @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頂層結(jié)構(gòu)圖

          tomcat最頂層容器是Server,代表著整個(gè)服務(wù)器,一個(gè)Server包含多個(gè)Service。從上圖可以看除Service主要包括多個(gè)Connector和一個(gè)Container。Connector用來處理連接相關(guān)的事情,并提供Socket到Request和Response相關(guān)轉(zhuǎn)化。

          Container用于封裝和管理Servlet,以及處理具體的Request請求。那么上文提到的Engine>Host>Context>Wrapper容器又是怎么回事呢?我們來看下圖:

          綜上所述,一個(gè)tomcat只包含一個(gè)Server,一個(gè)Server可以包含多個(gè)Service,一個(gè)Service只有一個(gè)Container,但有多個(gè)Connector,這樣一個(gè)服務(wù)可以處理多個(gè)連接。

          多個(gè)Connector和一個(gè)Container就形成了一個(gè)Service,有了Service就可以對外提供服務(wù)了,但是Service要提供服務(wù)又必須提供一個(gè)宿主環(huán)境,那就非Server莫屬了,所以整個(gè)tomcat的聲明周期都由Server控制。

          總結(jié)

          SpringBoot的啟動主要是通過實(shí)例化SpringApplication來啟動的,啟動過程主要做了以下幾件事情:配置屬性、獲取監(jiān)聽器,發(fā)布應(yīng)用開始啟動事件初、始化輸入?yún)?shù)、配置環(huán)境,輸出banner、創(chuàng)建上下文、預(yù)處理上下文、刷新上下文、再刷新上下文、發(fā)布應(yīng)用已經(jīng)啟動事件、發(fā)布應(yīng)用啟動完成事件。

          在SpringBoot中啟動tomcat的工作在刷新上下這一步。而tomcat的啟動主要是實(shí)例化兩個(gè)組件:Connector、Container,一個(gè)tomcat實(shí)例就是一個(gè)Server,一個(gè)Server包含多個(gè)Service,也就是多個(gè)應(yīng)用程序,每個(gè)Service包含多個(gè)Connector和一個(gè)Container,而一個(gè)Container下又包含多個(gè)子容器。


          END


          免費(fèi)領(lǐng)取 1000+ 道面試資料?。?/span>小編這里有一份面試寶典《Java 核心知識點(diǎn).pdf》,覆蓋了 JVM,鎖、高并發(fā)、Spring原理、微服務(wù)、數(shù)據(jù)庫、Zookeep人、數(shù)據(jù)結(jié)構(gòu)等等知識點(diǎn),包含 Java 后端知識點(diǎn) 1000+ 個(gè),部分如下:

          如何獲???加小編微信,回復(fù)【1024】

          瀏覽 40
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  三级小视频在线观看 | 太黄三级在线观看破处 | 国产日比视频 | 无码精品人妻喷潮一区二区三区白浆 | 日韩级黄色 |