SpringBoot內(nèi)置tomcat啟動(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)類
而其中我們常用的有兩個(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)圖
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容器又是怎么回事呢?我們來看下圖:
? ? ??
綜上所述,一個(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)注一下:
長按訂閱更多精彩▼
如有收獲,點(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)系我們,謝謝!