欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot內置Tomcat啟動方式

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

一、Tomcat相關配置類如何加載的?

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

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

1.進入Springboot啟動類

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

2.進入注解@EnableAutoConfiguration

如下圖:該注解通過@Import注解導入了AutoConfigurationImportSelector類。

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

3.進入AutoConfigurationImportSelector類

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

詳細講解如下:也就是下圖的,方法1調用方法2,方法2調用方法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服務器的配置類。

由于沒有導入jetty和undertow的相關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類

關注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");
		//設置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);
		//關閉熱部署
		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()方法在當前類,調用了getTomcatWebServer()方法,其實又new TomcatWebServer()對象:

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

7.進入TomcatWebServer類

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

(1)構造方法:調用了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()的調用分析,也就是tomcat何時啟動的

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

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

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

最終調用了本類的一個同名方法:

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、構造應用上下文環(huán)境】
			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
			//處理需要忽略的Bean
			configureIgnoreBeanInfo(environment);
			//打印banner
			Banner printedBanner = printBanner(environment);
			///【3、初始化應用上下文】
			context = createApplicationContext();
			//實例化SpringBootExceptionReporter.class,用來支持報告關于啟動的錯誤
			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			//【4、刷新應用上下文前的準備階段】
			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
			//【5、刷新應用上下文】
			refreshContext(context);
			//【6、刷新應用上下文后的擴展接口】
			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)構造容器環(huán)境,簡而言之就是加載系統(tǒng)變量,環(huán)境變量,配置文件
  • 3)創(chuàng)建容器
  • 4)實例化SpringBootExceptionReporter.class,用來支持報告關于啟動的錯誤
  • 5)準備容器
  • 6) 刷新容器
  • 7)刷新容器后的擴展接口

2.那么內置tomcat啟動源碼

就是隱藏在上面第六步:refreshContext方法里面,該方法最終會調 用到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()調用了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()方法調用了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() 方法調用了一個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)建該對象,就在構造方法啟動了Tomcat。詳細代碼在第一部分有。

總結

tomcat啟動流程

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

相關文章

  • java 高并發(fā)中volatile的實現(xiàn)原理

    java 高并發(fā)中volatile的實現(xiàn)原理

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

    詳細解讀Java的Lambda表達式

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

    MyBatis insert標簽及常用屬性詳解

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

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

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

    java集合進行排序的方式總結

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

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

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

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

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

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

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

    Java終止線程的兩種方法

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

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

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

最新評論