當(dāng)前位置:首頁 > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]不得不說SpringBoot的開發(fā)者是在為大眾程序猿謀福利,把大家都慣成了懶漢,xml不配置了,連tomcat也懶的配置了,典型的一鍵啟動(dòng)系統(tǒng),那么tomcat在springboot是怎么啟動(dòng)的呢?



前言


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


內(nèi)置tomcat


開發(fā)階段對(duì)我們來說使用內(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)用啟動(dò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)用啟動(dò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中是怎么啟動(dòng)的,那么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);
}


這里會(huì)創(chuàng)建AnnotationConfigServletWebServerApplicationContext類。


而AnnotationConfigServletWebServerApplicationContext類繼承了ServletWebServerApplicationContext,而這個(gè)類是最終集成了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)用各個(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,但是還沒有啟動(dòng)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)類


SpringBoot內(nèi)置tomcat啟動(dòng)原理

而其中我們常用的有兩個(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對(duì)象
??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源碼的話,對(duì)engine不會(huì)感到陌生。
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是最高級(jí)別容器,Host是Engine的子容器,Context是Host的子容器,Wrapper是Context的子容器


getWebServer這個(gè)方法創(chuàng)建了Tomcat對(duì)象,并且做了兩件重要的事情:把Connector對(duì)象添加到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 {
????//在控制臺(tái)會(huì)看到這句日志
??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();
????????}
??????});

??????//===啟動(dòng)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又會(huì)回到TomcatWebServer的
public?void?stop()?throws?LifecycleException {
??getServer();
??server.stop();
}


//TomcatWebServer.java
//啟動(dòng)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;
??????//在控制臺(tái)打印這句日志,如果在yml設(shè)置了上下文,這里會(huì)打印
??????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)圖


SpringBoot內(nèi)置tomcat啟動(dòng)原理

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請(qǐng)求。那么上文提到的Engine>Host>Context>Wrapper容器又是怎么回事呢?我們來看下圖:



SpringBoot內(nèi)置tomcat啟動(dòng)原理
? ? ??

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

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


總結(jié)


SpringBoot的啟動(dòng)主要是通過實(shí)例化SpringApplication來啟動(dòng)的,啟動(dòng)過程主要做了以下幾件事情:配置屬性、獲取監(jiān)聽器,發(fā)布應(yīng)用開始啟動(dòng)事件初、始化輸入?yún)?shù)、配置環(huán)境,輸出banner、創(chuàng)建上下文、預(yù)處理上下文、刷新上下文、再刷新上下文、發(fā)布應(yīng)用已經(jīng)啟動(dòng)事件、發(fā)布應(yīng)用啟動(dòng)完成事件。在SpringBoot中啟動(dòng)tomcat的工作在刷新上下這一步。而tomcat的啟動(dòng)主要是實(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è)子容器。


原文鏈接:https://www.cnblogs.com/sword-successful/p/11383723.html


特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:

SpringBoot內(nèi)置tomcat啟動(dòng)原理

SpringBoot內(nèi)置tomcat啟動(dòng)原理

SpringBoot內(nèi)置tomcat啟動(dòng)原理

長按訂閱更多精彩▼

SpringBoot內(nèi)置tomcat啟動(dòng)原理

如有收獲,點(diǎn)個(gè)在看,誠摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場,如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉