前言
不得不說SpringBoot的開發(fā)者是在為大眾程序猿謀福利,把大家都慣成了懶漢,xml不配置了,連tomcat也懶的配置了,典型的一鍵啟動系統(tǒng),那么tomcat在springboot是怎么啟動的呢?
內置tomcat
開發(fā)階段對我們來說使用內置的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ù)的做法還是排除內置的tomcat,打瓦包(war)然后部署在生產(chǎn)的tomcat中,好吧,那打包的時候應該怎么處理?
<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("===應用啟動耗時:"+(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) {
????}
??}
}
//這里直接調用最終父類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();
??????//調用各個子類的onRefresh()方法,也就說這里要回到子類:ServletWebServerApplicationContext,調用該類的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
//這里調用構造函數(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下又包含多個子容器。
原文鏈接:https://www.cnblogs.com/sword-successful/p/11383723.html
特別推薦一個分享架構+算法的優(yōu)質內容,還沒關注的小伙伴,可以長按關注一下:
長按訂閱更多精彩▼
如有收獲,點個在看,誠摯感謝
免責聲明:本文內容由21ic獲得授權后發(fā)布,版權歸原作者所有,本平臺僅提供信息存儲服務。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!





