最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot內(nèi)置Tomcat啟動方式

 更新時間:2024年12月10日 16:27:48   作者:孫振寧1999  
Spring Boot通過啟動類上的@EnableAutoConfiguration注解,自動生成并加載ServletWebServerFactoryAutoConfiguration類,該類通過@Import注解導(dǎo)入TomcatServletWebServerFactory類,該類在getWebServer()方法中創(chuàng)建并啟動TomcatServletWebServer對象

一、Tomcat相關(guān)配置類如何加載的?

在springboot項目中,我們只需要引入spring-boot-starter-web依賴,啟動服務(wù)成功,我們一個web服務(wù)就搭建好了,沒有明顯的看到tomcat。

其實打開spring-boot-starter-web依賴,我們可以看到:依賴了tomcat。

1.進入Springboot啟動類

我們加入Springboot最核心的注解@SpringBootApplication,源碼如下圖:重點看注解@EnableAutoConfiguration,

2.進入注解@EnableAutoConfiguration

如下圖:該注解通過@Import注解導(dǎo)入了AutoConfigurationImportSelector類。

其實這個類,就是導(dǎo)入通過加載配置文件,加載了很多工廠方法的配置類。

3.進入AutoConfigurationImportSelector類

首先調(diào)用selectImport()方法,在該方法中調(diào)用了 getAutoConfigurationEntry()方法,在之中又調(diào)用了getCandidateConfigurations()方法, getCandidateConfigurations()方法就去META-INF/spring.factory配置文件中加載相關(guān)配置類。

詳細講解如下:也就是下圖的,方法1調(diào)用方法2,方法2調(diào)用方法3:

到了這里加載了 META-INF/spring.factories文件:

4.我們看到

加載了ServletWebServerFactoryAutoConfiguration這個配置類,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 {
  ...
}

從這個配置工廠類,我們看出通過@Import注解加載了tomcat,jetty,undertow三個web服務(wù)器的配置類。

由于沒有導(dǎo)入jetty和undertow的相關(guān)jar包,這兩個類實例的不會真正的加載。

5.進入EmbeddedTomcat類

創(chuàng)建了TomcatServletWebServerFactory類的對象。

@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.進入TomcatServletWebServerFactory類

關(guān)注getWebServer()方法:

	@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		if (this.disableMBeanRegistry) {
			Registry.disableRegistry();
		}
		//實例化一個Tomcat
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
		//設(shè)置Tomcat的工作臨時目錄
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		//默認使用Http11NioProtocal實例化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);
		// 實例化TomcatWebServer時會將DispatcherServlet以及一些Filter添加到Tomcat中
		return getTomcatWebServer(tomcat);
	}

getWebServer()方法在當(dāng)前類,調(diào)用了getTomcatWebServer()方法,其實又new TomcatWebServer()對象:

	protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
		return new TomcatWebServer(tomcat, getPort() >= 0);
	}

7.進入TomcatWebServer類

這個類才是真正的做tomcat啟動的類:

(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)進入initialize()方法,這個方法:this.tomcat.start(),啟動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在這里啟動了
				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何時啟動的

上面分析了tomcat的配置到啟動的方法,我們現(xiàn)在來分析,tomcat是何時啟動的。

1.首先進入SpringBoot啟動類的run方法

	public static void main(String[] args) {
		SpringApplication.run(SpringBootMytestApplication.class, args);
	}

最終調(diào)用了本類的一個同名方法:

public ConfigurableApplicationContext run(String... args) {
		//記錄程序運行時間
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		// ConfigurableApplicationContext Spring 的上下文
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		//【1、獲取并啟動監(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();
			//實例化SpringBootExceptionReporter.class,用來支持報告關(guān)于啟動的錯誤
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			//【4、刷新應(yīng)用上下文前的準備階段】
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//【5、刷新應(yīng)用上下文】
			refreshContext(context);
			//【6、刷新應(yīng)用上下文后的擴展接口】
			afterRefresh(context, applicationArguments);
			//時間記錄停止
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
			}
			//發(fā)布容器啟動完成事件
			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;
	}

這個方法大概做了以下幾件事:

  • 1)獲取并啟動監(jiān)聽器 通過加載META-INF/spring.factories 完成了 SpringApplicationRunListener實例化工作
  • 2)構(gòu)造容器環(huán)境,簡而言之就是加載系統(tǒng)變量,環(huán)境變量,配置文件
  • 3)創(chuàng)建容器
  • 4)實例化SpringBootExceptionReporter.class,用來支持報告關(guān)于啟動的錯誤
  • 5)準備容器
  • 6) 刷新容器
  • 7)刷新容器后的擴展接口

2.那么內(nèi)置tomcat啟動源碼

就是隱藏在上面第六步:refreshContext方法里面,該方法最終會調(diào) 用到AbstractApplicationContext類的refresh()方法,進入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 {
			//核心方法:會獲取嵌入式的Servlet容器工廠,并通過工廠來獲取Servlet容器
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}

如下面的代碼:createWebServer() 方法調(diào)用了一個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對象,創(chuàng)建該對象,就在構(gòu)造方法啟動了Tomcat。詳細代碼在第一部分有。

總結(jié)

tomcat啟動流程

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java生成讀取條形碼和二維碼的簡單示例

    Java生成讀取條形碼和二維碼的簡單示例

    條形碼(barcode)是將寬度不等的多個黑條和空白,按照一定的規(guī)則排列,用來表示一組信息的圖形標識符,而二維碼大家應(yīng)該都很熟悉了,這篇文章主要給大家介紹了關(guān)于Java生成讀取條形碼和二維碼的相關(guān)資料,需要的朋友可以參考下
    2021-07-07
  • Java學(xué)習(xí)之異常處理的新特性詳解

    Java學(xué)習(xí)之異常處理的新特性詳解

    異常處理機制是Java很早時就搞出來的技術(shù),但在實際應(yīng)用中,我們發(fā)現(xiàn)這個異常處理也有一些不完善的地方,接下來主要給大家介紹一下Java異常處理的一些新特性,需要的朋友可以參考下
    2023-08-08
  • java配置數(shù)據(jù)庫連接池的方法步驟

    java配置數(shù)據(jù)庫連接池的方法步驟

    java配置數(shù)據(jù)庫連接池的方法步驟,需要的朋友可以參考一下
    2013-05-05
  • Java關(guān)鍵字finally_動力節(jié)點Java學(xué)院整理

    Java關(guān)鍵字finally_動力節(jié)點Java學(xué)院整理

    java關(guān)鍵字finally不管是否出現(xiàn)異常,finally子句總是在塊完成之前執(zhí)行。下面通過實現(xiàn)代碼給大家介紹Java關(guān)鍵字finally相關(guān)知識,需要的的朋友參考下吧
    2017-04-04
  • Java反轉(zhuǎn)鏈表測試過程介紹

    Java反轉(zhuǎn)鏈表測試過程介紹

    這篇文章主要介紹了Java反轉(zhuǎn)鏈表測試過程,學(xué)習(xí)過數(shù)據(jù)結(jié)構(gòu)的小伙伴們,對鏈表想來是并不陌生。本篇文章將為大家介紹幾種在Java語言當(dāng)中,實現(xiàn)鏈表反轉(zhuǎn)的幾種方法,以下是具體內(nèi)容
    2023-04-04
  • 詳解SpringBoot注冊Windows服務(wù)和啟動報錯的原因

    詳解SpringBoot注冊Windows服務(wù)和啟動報錯的原因

    這篇文章主要介紹了詳解SpringBoot注冊Windows服務(wù)和啟動報錯的原因,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03
  • SpringBoot啟動時如何修改上下文

    SpringBoot啟動時如何修改上下文

    本文介紹了如何在Spring Boot啟動時修改上下文,以便加載封裝JAR中的國際化文件,通過在resources目錄下的META-INF文件夾中的spring.factories文件中配置指定類,可以實現(xiàn)這一功能
    2024-11-11
  • Java使用延時隊列搞定超時訂單處理的場景

    Java使用延時隊列搞定超時訂單處理的場景

    這篇文章主要介紹了Java使用延時隊列搞定超時訂單處理,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • Java的Spring框架中實現(xiàn)發(fā)送郵件功能的核心代碼示例

    Java的Spring框架中實現(xiàn)發(fā)送郵件功能的核心代碼示例

    這篇文章主要介紹了Java的Spring框架中實現(xiàn)發(fā)送郵件功能的核心代碼示例,包括發(fā)送帶附件的郵件功能的實現(xiàn),需要的朋友可以參考下
    2016-03-03
  • Java 中EasyExcel的使用方式

    Java 中EasyExcel的使用方式

    這篇文章主要介紹了Java 中EasyExcel的使用方式,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下
    2022-08-08

最新評論

全州县| 海口市| 岳阳县| 拜城县| 镇安县| 清远市| 深圳市| 大名县| 贡嘎县| 库尔勒市| 铁岭市| 习水县| 龙泉市| 和平区| 汉源县| 奉化市| 阿拉善盟| 天水市| 那曲县| 锦州市| 长沙市| 渝北区| 子洲县| 突泉县| 察哈| 乐亭县| 太湖县| 云霄县| 清丰县| 五莲县| 开封市| 萍乡市| 巫溪县| 拜泉县| 定西市| 盐边县| 治县。| 宝坻区| 弋阳县| 吴旗县| 滨海县|