總在說SpringBoot內(nèi)置了tomcat啟動,那它的原理你說的清楚嗎?
回復(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


