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

SpringBoot的啟動過程源碼詳細(xì)分析

 更新時(shí)間:2023年11月18日 09:22:19   作者:morris131  
這篇文章主要介紹了SpringBoot的啟動過程源碼詳細(xì)分析,SpringBoot啟動的時(shí)候,會構(gòu)造一個(gè)SpringApplication的實(shí)例,構(gòu)造SpringApplication的時(shí)候會進(jìn)行初始化的工作,需要的朋友可以參考下

SpringBoot啟動過程源碼

程序的入口:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
	return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
	return new SpringApplication(primarySources).run(args);
}

最終會調(diào)用SpringApplication的構(gòu)造方法。

構(gòu)造SpringApplication對象

  • 推測web應(yīng)用類型
  • 獲取BootstrapRegistryInitializer對象
  • 獲取ApplicationContextInitializer對象
  • 獲取ApplicationListener對象
  • 推測出Main類(main()方法所在的類)
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
	this.resourceLoader = resourceLoader;
	Assert.notNull(primarySources, "PrimarySources must not be null");
	this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
	// 推測web應(yīng)用類型
	// 如果項(xiàng)目依賴中存在org.springframework.web.reactive.DispatcherHandler,并且不存在org.springframework.web.servlet.DispatcherServlet,那么應(yīng)用類型為WebApplicationType.REACTIVE
	// 如果項(xiàng)目依賴中不存在org.springframework.web.reactive.DispatcherHandler,也不存在org.springframework.web.servlet.DispatcherServlet,那么應(yīng)用類型為WebApplicationType.NONE
	// 否則,應(yīng)用類型為WebApplicationType.SERVLET
	this.webApplicationType = WebApplicationType.deduceFromClasspath();

	// 獲取BootstrapRegistryInitializer對象
	// 從"META-INF/spring.factories"中讀取key為BootstrapRegistryInitializer類型的擴(kuò)展點(diǎn),并實(shí)例化出對應(yīng)擴(kuò)展點(diǎn)對象
	// BootstrapRegistryInitializer的作用是可以初始化BootstrapRegistry
	// 下面的DefaultBootstrapContext對象就是一個(gè)BootstrapRegistry,可以用來注冊一些對象,這些對象可以用在從SpringBoot啟動到Spring容器初始化完成的過程中
	// 我的理解:沒有Spring容器之前就利用BootstrapRegistry來共享一些對象,有了Spring容器之后就利用Spring容器來共享一些對象

	this.bootstrapRegistryInitializers = new ArrayList<>(
		getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

	// 獲取ApplicationContextInitializer對象
	// 從"META-INF/spring.factories"中讀取key為ApplicationContextInitializer類型的擴(kuò)展點(diǎn),并實(shí)例化出對應(yīng)擴(kuò)展點(diǎn)對象
	// 顧名思義,ApplicationContextInitializer是用來初始化Spring容器ApplicationContext對象的,比如可以利用ApplicationContextInitializer來向Spring容器中添加ApplicationListener
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

	// 獲取ApplicationListener對象
	// 從"META-INF/spring.factories"中讀取key為ApplicationListener類型的擴(kuò)展點(diǎn),并實(shí)例化出對應(yīng)擴(kuò)展點(diǎn)對象
	// ApplicationListener是Spring中的監(jiān)聽器,并不是SpringBoot中的新概念,不多解釋了
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

	// 推測出Main類(main()方法所在的類)
	// 沒什么具體的作用,邏輯是根據(jù)當(dāng)前線程的調(diào)用棧來判斷main()方法在哪個(gè)類,哪個(gè)類就是Main類
	this.mainApplicationClass = deduceMainApplicationClass();
}

run(String… args)方法

run()主要負(fù)責(zé)Spring的啟動:

org.springframework.boot.SpringApplication#run(java.lang.String…)

public ConfigurableApplicationContext run(String... args) {
	long startTime = System.nanoTime();
	// 創(chuàng)建DefaultBootstrapContext對象
	// 用BootstrapRegistryInitializer初始化DefaultBootstrapContext對象
	// DefaultBootstrapContext用來在spring容器創(chuàng)建之前共享對象
	DefaultBootstrapContext bootstrapContext = createBootstrapContext();
	ConfigurableApplicationContext context = null;
	configureHeadlessProperty();

	// 從"META-INF/spring.factories"中讀取key為SpringApplicationRunListener類型的擴(kuò)展點(diǎn),并實(shí)例化出對應(yīng)擴(kuò)展點(diǎn)對象
	SpringApplicationRunListeners listeners = getRunListeners(args);
	// 觸發(fā)SpringApplicationRunListener的starting()
	// 默認(rèn)情況下SpringBoot提供了一個(gè)EventPublishingRunListener,發(fā)布ApplicationStartingEvent事件
	listeners.starting(bootstrapContext, this.mainApplicationClass);
	try {
		ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
		// Environment對象表示環(huán)境變量,該對象內(nèi)部主要包含了
		// 1. 當(dāng)前操作系統(tǒng)的環(huán)境變量
		// 2. JVM的一些配置信息
		// 3. -D方式所配置的JVM環(huán)境變量
		ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);


		configureIgnoreBeanInfo(environment);

		// 打印banner
		Banner printedBanner = printBanner(environment);

		// 根據(jù)webApplicationType不同創(chuàng)建不同的ApplicationContext
		context = createApplicationContext();
		context.setApplicationStartup(this.applicationStartup);

		// refreshContext前的準(zhǔn)備工作,例如注入一些配置Bean
		prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

		// 調(diào)用applicationContext.refresh();
		refreshContext(context);

		// 空方法
		afterRefresh(context, applicationArguments);

		Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
		if (this.logStartupInfo) {
			// 打印啟動時(shí)間日志
			new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
		}
		// 發(fā)布ApplicationStartedEvent事件
		// 發(fā)布AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示狀態(tài)變更狀態(tài),變更后的狀態(tài)為LivenessState.CORRECT
		listeners.started(context, timeTakenToStartup);

		// 調(diào)用ApplicationRunner.run
		// 調(diào)用CommandLineRunner.run
		callRunners(context, applicationArguments);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, listeners);
		throw new IllegalStateException(ex);
	}
	try {
		Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
		// 發(fā)布ApplicationReadyEvent事件
		// 發(fā)布AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示狀態(tài)變更狀態(tài),變更后的狀態(tài)為ReadinessState.ACCEPTING_TRAFFIC
		listeners.ready(context, timeTakenToReady);
	}
	catch (Throwable ex) {
		handleRunFailure(context, ex, null);
		throw new IllegalStateException(ex);
	}
	return context;
}

創(chuàng)建Environment對象

Environment對象表示環(huán)境變量,該對象內(nèi)部主要包含了:

  • 當(dāng)前操作系統(tǒng)的環(huán)境變量
  • JVM的一些配置信息
  • -D方式所配置的JVM環(huán)境變量

org.springframework.boot.SpringApplication#prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
												   DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
	// Create and configure the environment
	// 創(chuàng)建環(huán)境變量,一般是ApplicationServletEnvironment
	ConfigurableEnvironment environment = getOrCreateEnvironment();
	// 向environment中添加defaultProperties
	// 將命令行的參數(shù)封裝為SimpleCommandLinePropertySource,加入environment
	configureEnvironment(environment, applicationArguments.getSourceArgs());
	// 添加ConfigurationPropertySourcesPropertySource到environment
	ConfigurationPropertySources.attach(environment);

	// 默認(rèn)情況下會利用EventPublishingRunListener發(fā)布ApplicationEnvironmentPreparedEvent事件
	// 比如默認(rèn)情況下會有一個(gè)EnvironmentPostProcessorApplicationListener來消費(fèi)這個(gè)事件
	// 而這個(gè)ApplicationListener接收到這個(gè)事件之后,就會解析application.properties、application.yml文件,并添加到Environment對象中去。
	/**
		 * @see EnvironmentPostProcessorApplicationListener
		 */
	listeners.environmentPrepared(bootstrapContext, environment);
	// 將DefaultPropertiesPropertySource移到最后
	DefaultPropertiesPropertySource.moveToEnd(environment);
	Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
				 "Environment prefix cannot be set via properties.");
	bindToSpringApplication(environment);
	if (!this.isCustomEnvironment) {
		environment = convertEnvironment(environment);
	}
	// 為什么又調(diào)用一次?
	ConfigurationPropertySources.attach(environment);
	return environment;
}

創(chuàng)建Spring容器對象(ApplicationContext)

會利用ApplicationContextFactory.DEFAULT,根據(jù)應(yīng)用類型創(chuàng)建對應(yīng)的Spring容器。

ApplicationContextFactory.DEFAULT為:

ApplicationContextFactory DEFAULT = (webApplicationType) -> {
	try {
		/**
			 * org.springframework.boot.ApplicationContextFactory=\
			 * org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext.Factory,\
			 * org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext.Factory
			 */
		for (ApplicationContextFactory candidate : SpringFactoriesLoader
			 .loadFactories(ApplicationContextFactory.class, ApplicationContextFactory.class.getClassLoader())) {
			// 創(chuàng)建的是AnnotationConfigServletWebServerApplicationContext
			ConfigurableApplicationContext context = candidate.create(webApplicationType);
			if (context != null) {
				return context;
			}
		}
		return new AnnotationConfigApplicationContext();
	}
	catch (Exception ex) {
		throw new IllegalStateException("Unable create a default ApplicationContext instance, "
										+ "you may need a custom ApplicationContextFactory", ex);
	}
};

所以:

  • 應(yīng)用類型為SERVLET,則對應(yīng)AnnotationConfigServletWebServerApplicationContext
  • 應(yīng)用類型為REACTIVE,則對應(yīng)AnnotationConfigReactiveWebServerApplicationContext
  • 應(yīng)用類型為普通類型,則對應(yīng)AnnotationConfigApplicationContext

利用ApplicationContextInitializer初始化Spring容器對象

默認(rèn)情況下SpringBoot提供了多個(gè)ApplicationContextInitializer,其中比較重要的有ConditionEvaluationReportLoggingListener,別看到它的名字叫XXXListener,但是它確實(shí)是實(shí)現(xiàn)了ApplicationContextInitializer接口的。

org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener#initialize

@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
	this.applicationContext = applicationContext;
	applicationContext.addApplicationListener(new ConditionEvaluationReportListener());
	if (applicationContext instanceof GenericApplicationContext) {
		// Get the report early in case the context fails to load
		this.report = ConditionEvaluationReport.get(this.applicationContext.getBeanFactory());
	}
}

在它的initialize()方法中會:

  1. 將Spring容器賦值給它的applicationContext屬性
  2. 并且往Spring容器中添加一個(gè)ConditionEvaluationReportListener(ConditionEvaluationReportLoggingListener的內(nèi)部類),它是一個(gè)ApplicationListener
  3. 并生成一個(gè)ConditionEvaluationReport對象賦值給它的report屬性

ConditionEvaluationReportListener會負(fù)責(zé)接收ContextRefreshedEvent事件,也就是Spring容器一旦啟動完畢就會觸發(fā)ContextRefreshedEvent,ConditionEvaluationReportListener就會打印自動配置類的條件評估報(bào)告。

觸發(fā)SpringApplicationRunListener的contextPrepared()

默認(rèn)情況下會利用EventPublishingRunListener發(fā)布一個(gè)ApplicationContextInitializedEvent事件,默認(rèn)情況下暫時(shí)沒有ApplicationListener消費(fèi)了這個(gè)事件

調(diào)用DefaultBootstrapContext對象的close()

沒什么特殊的,忽略

將啟動類作為配置類注冊到Spring容器中(load()方法)

將SpringApplication.run(MyApplication.class);中傳入進(jìn)來的類,比如MyApplication.class,作為Spring容器的配置類

觸發(fā)SpringApplicationRunListener的contextLoaded()

默認(rèn)情況下會利用EventPublishingRunListener發(fā)布一個(gè)ApplicationPreparedEvent事件

刷新Spring容器

調(diào)用Spring容器的refresh()方法,相當(dāng)于執(zhí)行了這樣一個(gè)流程:

AnnotationConfigServletWebServerApplicationContext applicationContext = new AnnotationConfigServletWebServerApplicationContext();
applicationContext .register(MyApplication.class)
applicationContext .refresh()

觸發(fā)SpringApplicationRunListener的started()

發(fā)布ApplicationStartedEvent事件和AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示狀態(tài)變更狀態(tài),變更后的狀態(tài)為LivenessState.CORRECT

LivenessState枚舉有兩個(gè)值:

  • CORRECT:表示當(dāng)前應(yīng)用正常運(yùn)行中
  • BROKEN:表示當(dāng)前應(yīng)用還在運(yùn)行,但是內(nèi)部出現(xiàn)問題,暫時(shí)還沒發(fā)現(xiàn)哪里用到了

調(diào)用ApplicationRunner和CommandLineRunner

  1. 獲取Spring容器中的ApplicationRunner類型的Bean
  2. 獲取Spring容器中的CommandLineRunner類型的Bean
  3. 執(zhí)行它們的run()

觸發(fā)SpringApplicationRunListener的ready()

發(fā)布ApplicationReadyEvent事件和AvailabilityChangeEvent事件,AvailabilityChangeEvent事件表示狀態(tài)變更狀態(tài),變更后的狀態(tài)為ReadinessState.ACCEPTING_TRAFFIC

ReadinessState枚舉有兩個(gè)值:

ACCEPTING_TRAFFIC:表示當(dāng)前應(yīng)用準(zhǔn)備接收請求

REFUSING_TRAFFIC:表示當(dāng)前應(yīng)用拒絕接收請求,比如Tomcat關(guān)閉時(shí),就會發(fā)布AvailabilityChangeEvent事件,并且狀態(tài)為REFUSING_TRAFFIC

上述過程拋異常了就觸發(fā)SpringApplicationRunListener的failed()

發(fā)布ApplicationFailedEvent事件。

到此這篇關(guān)于SpringBoot的啟動過程源碼詳細(xì)分析的文章就介紹到這了,更多相關(guān)SpringBoot啟動過程源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java類的繼承原理與用法分析

    Java類的繼承原理與用法分析

    這篇文章主要介紹了Java類的繼承原理與用法,結(jié)合實(shí)例形式分析了java類的繼承相關(guān)原理、使用方法及操作注意事項(xiàng),需要的朋友可以參考下
    2020-02-02
  • 從SpringBoot打war包并配置外部Tomcat運(yùn)行的全流程

    從SpringBoot打war包并配置外部Tomcat運(yùn)行的全流程

    由于其他原因,我們需要使用SpringBoot打成war包放在外部的Tomcat中運(yùn)行,本文就以一個(gè)案例來說明從SpringBoot打war包到Tomcat配置并運(yùn)行的全流程經(jīng)過,需要的朋友可以參考下
    2024-06-06
  • java限流算法詳細(xì)

    java限流算法詳細(xì)

    這篇文章詳細(xì)的介紹了java限流算法常用到的算法計(jì)數(shù)算法、漏桶算法、令牌桶等算法的相關(guān)資料,需要的朋友可以參考下文,希望本篇文章能幫助到您
    2021-09-09
  • Java基礎(chǔ)之CardLayout的使用

    Java基礎(chǔ)之CardLayout的使用

    這篇文章主要介紹了Java基礎(chǔ)之CardLayout的使用,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-05-05
  • logback配置中變量和include的應(yīng)用方式

    logback配置中變量和include的應(yīng)用方式

    這篇文章主要介紹了logback配置中變量和include的應(yīng)用方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Java面試題沖刺第六天--網(wǎng)絡(luò)編程1

    Java面試題沖刺第六天--網(wǎng)絡(luò)編程1

    這篇文章主要為大家分享了最有價(jià)值的三道網(wǎng)絡(luò)編程面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下
    2021-07-07
  • Java根據(jù)前端傳回的圖片生成pdf并且加密碼和水印

    Java根據(jù)前端傳回的圖片生成pdf并且加密碼和水印

    這篇文章主要為大家詳細(xì)介紹了java如何根據(jù)前端傳回的png圖片數(shù)組,后端加水印加密碼生成pdf并返回給前端,感興趣的小伙伴可以參考一下
    2025-01-01
  • 詳解Java8與Runtime.getRuntime().availableProcessors()

    詳解Java8與Runtime.getRuntime().availableProcessors()

    這篇文章主要介紹了詳解Java8與Runtime.getRuntime().availableProcessors(),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • JAVA比較兩張圖片相似度的方法

    JAVA比較兩張圖片相似度的方法

    這篇文章主要介紹了JAVA比較兩張圖片相似度的方法,涉及java針對圖片像素操作的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • Java實(shí)現(xiàn)二分查找樹及其相關(guān)操作

    Java實(shí)現(xiàn)二分查找樹及其相關(guān)操作

    二分查找樹是一種有組織的二叉樹。我們可以通過鏈接節(jié)點(diǎn)表示這樣一棵樹,二分查找樹(Binary Search Tree)的基本操作有搜索、求最大值、求最小值、求前驅(qū)、求后繼、插入及刪除,對java二分查找樹相關(guān)知識感興趣的朋友一起看看吧
    2021-07-07

最新評論

朝阳市| 瑞丽市| 宽甸| 旌德县| 德保县| 绍兴市| 宁安市| 会东县| 青河县| 宜宾市| 会泽县| 洪雅县| 渭源县| 左权县| 扶风县| 三都| 哈密市| 山西省| 日喀则市| 彰化市| 汶川县| 潢川县| 胶州市| 临夏县| 福建省| 雷波县| 阳朔县| 凉城县| 漯河市| 开平市| 商洛市| 台北县| 成安县| 当涂县| 伊春市| 仁寿县| 望谟县| 肥东县| 章丘市| 丽水市| 金寨县|