亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

一、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)原理

    這篇文章主要介紹了java 高并發(fā)中volatile的實(shí)現(xiàn)原理的相關(guān)資料,在多線程并發(fā)編程中synchronized和Volatile都扮演著重要的角色,Volatile是輕量級(jí)的synchronized,它在多處理器開發(fā)中保證了共享變量的“可見性”,需要的朋友可以參考下
    2017-03-03
  • 詳細(xì)解讀Java的Lambda表達(dá)式

    詳細(xì)解讀Java的Lambda表達(dá)式

    這篇文章主要介紹了詳細(xì)解讀Java的Lambda表達(dá)式,lambda?表達(dá)式?是Java?8新加入的新特性,它在Java中是引入了函數(shù)式編程這一概念,需要的朋友可以參考下
    2023-04-04
  • MyBatis insert標(biāo)簽及常用屬性詳解

    MyBatis insert標(biāo)簽及常用屬性詳解

    這篇文章主要介紹了MyBatis insert標(biāo)簽,insert 標(biāo)簽中沒有 resultType 屬性,只有查詢操作才需要對(duì)返回結(jié)果類型進(jìn)行相應(yīng)的指定,需要的朋友可以參考下
    2023-10-10
  • navicatdesignquery.sql.bak系統(tǒng)找不到指定路徑錯(cuò)誤的解決方法

    navicatdesignquery.sql.bak系統(tǒng)找不到指定路徑錯(cuò)誤的解決方法

    今天小編就為大家分享一篇關(guān)于navicatdesignquery.sql.bak系統(tǒng)找不到指定路徑錯(cuò)誤的解決方法,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • java集合進(jìn)行排序的方式總結(jié)

    java集合進(jìn)行排序的方式總結(jié)

    在本篇文章里小編給大家整理的是一篇關(guān)于java集合進(jìn)行排序的兩種方式總結(jié),有興趣的朋友們可以學(xué)習(xí)參考下。
    2021-08-08
  • 在idea中如何使用Typora編輯markdown文件

    在idea中如何使用Typora編輯markdown文件

    這篇文章主要介紹了在idea中如何使用Typora編輯markdown文件問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • java實(shí)現(xiàn)拼圖游戲

    java實(shí)現(xiàn)拼圖游戲

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)拼圖游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • list轉(zhuǎn)tree和list中查找某節(jié)點(diǎn)下的所有數(shù)據(jù)操作

    list轉(zhuǎn)tree和list中查找某節(jié)點(diǎn)下的所有數(shù)據(jù)操作

    這篇文章主要介紹了list轉(zhuǎn)tree和list中查找某節(jié)點(diǎn)下的所有數(shù)據(jù)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Java終止線程的兩種方法

    Java終止線程的兩種方法

    本文主要介紹了Java終止線程的兩種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • SpringBoot實(shí)現(xiàn)過濾器攔截器的耗時(shí)對(duì)比

    SpringBoot實(shí)現(xiàn)過濾器攔截器的耗時(shí)對(duì)比

    這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)過濾器攔截器的輸出接口耗時(shí)對(duì)比,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-06-06

最新評(píng)論