SpringBoot內(nèi)置Tomcat啟動(dòng)方式
一、Tomcat相關(guān)配置類如何加載的?
在springboot項(xiàng)目中,我們只需要引入spring-boot-starter-web依賴,啟動(dòng)服務(wù)成功,我們一個(gè)web服務(wù)就搭建好了,沒有明顯的看到tomcat。
其實(shí)打開spring-boot-starter-web依賴,我們可以看到:依賴了tomcat。
1.進(jìn)入Springboot啟動(dòng)類
我們加入Springboot最核心的注解@SpringBootApplication,源碼如下圖:重點(diǎn)看注解@EnableAutoConfiguration,
2.進(jìn)入注解@EnableAutoConfiguration
如下圖:該注解通過@Import注解導(dǎo)入了AutoConfigurationImportSelector類。
其實(shí)這個(gè)類,就是導(dǎo)入通過加載配置文件,加載了很多工廠方法的配置類。
3.進(jìn)入AutoConfigurationImportSelector類
首先調(diào)用selectImport()方法,在該方法中調(diào)用了 getAutoConfigurationEntry()方法,在之中又調(diào)用了getCandidateConfigurations()方法, getCandidateConfigurations()方法就去META-INF/spring.factory配置文件中加載相關(guān)配置類。
詳細(xì)講解如下:也就是下圖的,方法1調(diào)用方法2,方法2調(diào)用方法3:
到了這里加載了 META-INF/spring.factories文件:
4.我們看到
加載了ServletWebServerFactoryAutoConfiguration這個(gè)配置類,web工廠配置類。
@Configuration(proxyBeanMethods = false) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, ServletWebServerFactoryConfiguration.EmbeddedJetty.class, ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { ... }
從這個(gè)配置工廠類,我們看出通過@Import注解加載了tomcat,jetty,undertow三個(gè)web服務(wù)器的配置類。
由于沒有導(dǎo)入jetty和undertow的相關(guān)jar包,這兩個(gè)類實(shí)例的不會(huì)真正的加載。
5.進(jìn)入EmbeddedTomcat類
創(chuàng)建了TomcatServletWebServerFactory類的對(duì)象。
@Configuration(proxyBeanMethods = false) class ServletWebServerFactoryConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) static class EmbeddedTomcat { @Bean TomcatServletWebServerFactory tomcatServletWebServerFactory( ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); factory.getTomcatConnectorCustomizers() .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatContextCustomizers() .addAll(contextCustomizers.orderedStream().collect(Collectors.toList())); factory.getTomcatProtocolHandlerCustomizers() .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList())); return factory; } }
6.進(jìn)入TomcatServletWebServerFactory類
關(guān)注getWebServer()方法:
@Override public WebServer getWebServer(ServletContextInitializer... initializers) { if (this.disableMBeanRegistry) { Registry.disableRegistry(); } //實(shí)例化一個(gè)Tomcat Tomcat tomcat = new Tomcat(); File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat"); //設(shè)置Tomcat的工作臨時(shí)目錄 tomcat.setBaseDir(baseDir.getAbsolutePath()); //默認(rèn)使用Http11NioProtocal實(shí)例化Connector Connector connector = new Connector(this.protocol); connector.setThrowOnFailure(true); //給Service添加Connector tomcat.getService().addConnector(connector); customizeConnector(connector); tomcat.setConnector(connector); //關(guān)閉熱部署 tomcat.getHost().setAutoDeploy(false); //配置Engine configureEngine(tomcat.getEngine()); for (Connector additionalConnector : this.additionalTomcatConnectors) { tomcat.getService().addConnector(additionalConnector); } prepareContext(tomcat.getHost(), initializers); // 實(shí)例化TomcatWebServer時(shí)會(huì)將DispatcherServlet以及一些Filter添加到Tomcat中 return getTomcatWebServer(tomcat); }
getWebServer()方法在當(dāng)前類,調(diào)用了getTomcatWebServer()方法,其實(shí)又new TomcatWebServer()對(duì)象:
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) { return new TomcatWebServer(tomcat, getPort() >= 0); }
7.進(jìn)入TomcatWebServer類
這個(gè)類才是真正的做tomcat啟動(dòng)的類:
(1)構(gòu)造方法:調(diào)用了initialize()方法
public TomcatWebServer(Tomcat tomcat, boolean autoStart) { Assert.notNull(tomcat, "Tomcat Server must not be null"); this.tomcat = tomcat; this.autoStart = autoStart; initialize(); }
(2)進(jìn)入initialize()方法,這個(gè)方法:this.tomcat.start(),啟動(dòng)tomcat容器了。
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())) { // Remove service connectors so that protocol binding doesn't // happen when the service is started. removeServiceConnectors(); } }); // Tomcat在這里啟動(dòng)了 this.tomcat.start(); // We can re-throw failure exception directly in the main thread rethrowDeferredStartupExceptions(); try { ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } catch (NamingException ex) { // Naming is not enabled. Continue } // Unlike Jetty, all Tomcat threads are daemon threads. We create a // blocking non-daemon to stop immediate shutdown startDaemonAwaitThread(); } catch (Exception ex) { stopSilently(); destroySilently(); throw new WebServerException("Unable to start embedded Tomcat", ex); } } }
二、getWebServer()的調(diào)用分析,也就是tomcat何時(shí)啟動(dòng)的
上面分析了tomcat的配置到啟動(dòng)的方法,我們現(xiàn)在來分析,tomcat是何時(shí)啟動(dòng)的。
1.首先進(jìn)入SpringBoot啟動(dòng)類的run方法
public static void main(String[] args) { SpringApplication.run(SpringBootMytestApplication.class, args); }
最終調(diào)用了本類的一個(gè)同名方法:
public ConfigurableApplicationContext run(String... args) { //記錄程序運(yùn)行時(shí)間 StopWatch stopWatch = new StopWatch(); stopWatch.start(); // ConfigurableApplicationContext Spring 的上下文 ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); //【1、獲取并啟動(dòng)監(jiān)聽器】 SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //【2、構(gòu)造應(yīng)用上下文環(huán)境】 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //處理需要忽略的Bean configureIgnoreBeanInfo(environment); //打印banner Banner printedBanner = printBanner(environment); ///【3、初始化應(yīng)用上下文】 context = createApplicationContext(); //實(shí)例化SpringBootExceptionReporter.class,用來支持報(bào)告關(guān)于啟動(dòng)的錯(cuò)誤 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); //【4、刷新應(yīng)用上下文前的準(zhǔn)備階段】 prepareContext(context, environment, listeners, applicationArguments, printedBanner); //【5、刷新應(yīng)用上下文】 refreshContext(context); //【6、刷新應(yīng)用上下文后的擴(kuò)展接口】 afterRefresh(context, applicationArguments); //時(shí)間記錄停止 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //發(fā)布容器啟動(dòng)完成事件 listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
這個(gè)方法大概做了以下幾件事:
- 1)獲取并啟動(dòng)監(jiān)聽器 通過加載META-INF/spring.factories 完成了 SpringApplicationRunListener實(shí)例化工作
- 2)構(gòu)造容器環(huán)境,簡(jiǎn)而言之就是加載系統(tǒng)變量,環(huán)境變量,配置文件
- 3)創(chuàng)建容器
- 4)實(shí)例化SpringBootExceptionReporter.class,用來支持報(bào)告關(guān)于啟動(dòng)的錯(cuò)誤
- 5)準(zhǔn)備容器
- 6) 刷新容器
- 7)刷新容器后的擴(kuò)展接口
2.那么內(nèi)置tomcat啟動(dòng)源碼
就是隱藏在上面第六步:refreshContext方法里面,該方法最終會(huì)調(diào) 用到AbstractApplicationContext類的refresh()方法,進(jìn)入refreshContext()方法,如圖:
private void refreshContext(ConfigurableApplicationContext context) { refresh(context); if (this.registerShutdownHook) { try { context.registerShutdownHook(); } catch (AccessControlException ex) { // Not allowed in some environments. } } }
refreshContext()調(diào)用了refresh()方法:
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(); this.onRefresh(); this.registerListeners(); this.finishBeanFactoryInitialization(beanFactory); this.finishRefresh(); } catch (BeansException var9) { if (this.logger.isWarnEnabled()) { this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9); } this.destroyBeans(); this.cancelRefresh(var9); throw var9; } finally { this.resetCommonCaches(); } } }
refresh()方法調(diào)用了this.onRefresh():
@Override protected void onRefresh() { super.onRefresh(); try { //核心方法:會(huì)獲取嵌入式的Servlet容器工廠,并通過工廠來獲取Servlet容器 createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
如下面的代碼:createWebServer() 方法調(diào)用了一個(gè)factory.getWebServer()。
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { //先獲取嵌入式Servlet容器工廠 ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
到了這里getWebServer()方法,下一步就是創(chuàng)建TomcatWebServer對(duì)象,創(chuàng)建該對(duì)象,就在構(gòu)造方法啟動(dòng)了Tomcat。詳細(xì)代碼在第一部分有。
總結(jié)
tomcat啟動(dòng)流程
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java 高并發(fā)中volatile的實(shí)現(xiàn)原理
這篇文章主要介紹了java 高并發(fā)中volatile的實(shí)現(xiàn)原理的相關(guān)資料,在多線程并發(fā)編程中synchronized和Volatile都扮演著重要的角色,Volatile是輕量級(jí)的synchronized,它在多處理器開發(fā)中保證了共享變量的“可見性”,需要的朋友可以參考下2017-03-03navicatdesignquery.sql.bak系統(tǒng)找不到指定路徑錯(cuò)誤的解決方法
今天小編就為大家分享一篇關(guān)于navicatdesignquery.sql.bak系統(tǒng)找不到指定路徑錯(cuò)誤的解決方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-12-12list轉(zhuǎn)tree和list中查找某節(jié)點(diǎn)下的所有數(shù)據(jù)操作
這篇文章主要介紹了list轉(zhuǎn)tree和list中查找某節(jié)點(diǎn)下的所有數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09SpringBoot實(shí)現(xiàn)過濾器攔截器的耗時(shí)對(duì)比
這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)過濾器攔截器的輸出接口耗時(shí)對(duì)比,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-06-06