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

SpringBoot?啟動(dòng)流程追蹤方法分享

 更新時(shí)間:2023年08月10日 08:42:40   作者:M-Anonymous  
這篇文章主要介紹了SpringBoot?啟動(dòng)流程追蹤方法分享的相關(guān)資料,需要的朋友可以參考下

1、初始化 SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		this.bootstrapRegistryInitializers = new ArrayList<>(
				getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		this.mainApplicationClass = deduceMainApplicationClass();
	}

在完成初始化工作后,可以看到設(shè)置了如下屬性:bootstrapRegistryInitializers:

initializers:

listeners:

這些屬性咋來(lái)的上一篇文章中有提到過(guò),會(huì)掃描 spring-boot、spring-boot-autoconfigure、spring-beans 包里面 resource 目錄下 META-INF/spring.factories 文件進(jìn)行加載,如果你想添加自己的配置,也可以在自己項(xiàng)目的 resource 目錄下添加配置。

2、加載 spring.factories

	private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

加載結(jié)果如下所示:

以后有些地方加載類的時(shí)候,就會(huì)直接從緩存取了。

3、環(huán)境準(zhǔn)備前的工作(run 方法代碼片段)

long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);

在 createBootstrapContext 方法里面會(huì)調(diào)用 bootstrapRegistryInitializers 的 initializer 方法,不過(guò) SpringBoot 該屬性沒(méi)值。然后會(huì)調(diào)用 getRunListeners 方法加載 SpringApplicationRunListeners,該值同樣也是從 spring.factories 文件進(jìn)行加載的。該 listeners(SpringApplicationRunListeners) 下的 SpringApplicationRunListener 只有一個(gè): EventPublishingRunListener

然后會(huì)調(diào)用該類的 starting 方法,會(huì)觸發(fā) ApplicationStartingEvent 事件,該事件會(huì)被 SpringApplication 下的 listeners 監(jiān)聽(tīng)。如果你感興趣的話,可以看看 8 個(gè) ApplicationListener 干了什么!

4、準(zhǔn)備環(huán)境(run->prepareEnvironment)

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(bootstrapContext, environment);
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}

這里是先創(chuàng)建一個(gè)默認(rèn)的 Servlet 環(huán)境,然后為該環(huán)境配置參數(shù),在 configureEnvironment ->configurePropertySources 方法中可以看到會(huì)從命令行參數(shù)和 SpringApplication 的 defaultProperties 屬性獲取可配置參數(shù)。環(huán)境準(zhǔn)備好后,會(huì)執(zhí)行 listeners.environmentPrepared 方法,上文提到過(guò),該方法只有一個(gè)實(shí)現(xiàn)類,調(diào)用該方法會(huì)觸發(fā) ApplicationEnvironmentPreparedEvent 事件,同樣也會(huì)被監(jiān)聽(tīng)到。該方法目前就看這兩個(gè)就行了,其他的方法不知道在哪兒用的,看了也說(shuō)不明白。

5、配置 Banner 和 上下文

Banner printedBanner = printBanner(environment);
context = createApplicationContext();

如果想要知道怎么自定義 Banner,可以看 printBanner,通過(guò)創(chuàng)建 banner.txt 文本格式或 banner.png、banner.gif、banner.gif 等圖片格式文件,可實(shí)現(xiàn)自定義 banner,文件默認(rèn)放在 resource 目錄下就行,如果不嫌麻煩的話也可以自定義 banner 存放目錄。接下來(lái)是 context 上下文,這在后面會(huì)經(jīng)常用到,它會(huì)使用默認(rèn)的 contextFactory 來(lái)創(chuàng)建 context,并且它是通過(guò) loadSpringFactories 方法來(lái)獲取的,其實(shí)現(xiàn)類在 spring.factories 里配置的是 AnnotationConfigServletWebServerApplicationContext。

6、準(zhǔn)備上下文(run->prepareContext)

	private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
			ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
			ApplicationArguments applicationArguments, Banner printedBanner) {
		context.setEnvironment(environment);
		postProcessApplicationContext(context);
		applyInitializers(context);
		listeners.contextPrepared(context);
		bootstrapContext.close(context);
		if (this.logStartupInfo) {
			logStartupInfo(context.getParent() == null);
			logStartupProfileInfo(context);
		}
		// Add boot specific singleton beans
		ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
		beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
		if (printedBanner != null) {
			beanFactory.registerSingleton("springBootBanner", printedBanner);
		}
		if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
			((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
			if (beanFactory instanceof DefaultListableBeanFactory) {
				((DefaultListableBeanFactory) beanFactory)
					.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
			}
		}
		if (this.lazyInitialization) {
			context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
		}
		context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
		// Load the sources
		Set<Object> sources = getAllSources();
		Assert.notEmpty(sources, "Sources must not be empty");
		load(context, sources.toArray(new Object[0]));
		listeners.contextLoaded(context);
	}

這里調(diào)用了 applyInitializers 方法,之前 SpringApplication 加載了 7 個(gè) ApplicationContextInitializer,這里會(huì)調(diào)用每個(gè) initializer 的 initialize 方法。接著就調(diào)用 listeners 的 contextPrepared 方法,還是之前的 EventPublishingRunListener,該方法會(huì)觸發(fā) 8 個(gè) ApplicationListener 監(jiān)聽(tīng) ApplicationContextInitializedEvent 事件。在這之后的 bootstrapContext.close 方法也會(huì) BootstrapContextClosedEvent 事件。然后想 set、add、log 啥的可以直接跳過(guò),beanFactory.registerSingleton 方法可以點(diǎn)進(jìn)去看看,不過(guò)也是 add 啥的,這些其實(shí)都是為后面實(shí)質(zhì)性的操作做準(zhǔn)備,后面可以再追溯數(shù)據(jù)來(lái)源。接著看看 load 方法,load 方法的 source 來(lái)源一個(gè)是 primarySources,另一個(gè)是 sources,都是 SpringApplication 的屬性,該方法可以加載 Bean,并且該方法細(xì)節(jié)也是蠻多的,這里先記著,后面再看。最后就是 listeners.contextLoaded 方法了,該方法會(huì)觸發(fā) ApplicationPreparedEvent 事件。

7、添加上下文銷毀線程(refreshcontext...->addRuntimeShutdownHook)

	void addRuntimeShutdownHook() {
try {
Runtime.getRuntime().addShutdownHook(new Thread(this, "SpringApplicationShutdownHook"));
}
catch (AccessControlException ex) {
// Not allowed in some environments
}
}
@Override
public void run() {
Set<ConfigurableApplicationContext> contexts;
Set<ConfigurableApplicationContext> closedContexts;
Set<Runnable> actions;
synchronized (SpringApplicationShutdownHook.class) {
this.inProgress = true;
contexts = new LinkedHashSet<>(this.contexts);
closedContexts = new LinkedHashSet<>(this.closedContexts);
actions = new LinkedHashSet<>(this.handlers.getActions());
}
contexts.forEach(this::closeAndWait);
closedContexts.forEach(this::closeAndWait);
actions.forEach(Runnable::run);
}

這里在線程末尾會(huì)執(zhí)行上下文的 closeAndWait 方法,以及支持自定義的 actions。

8、收尾工作

在刷新完 context 之后,會(huì)執(zhí)行 listeners.started、ready 方法,分別會(huì)觸發(fā) ApplicationStartedEvent、ApplicationReadyEvent 事件。同樣會(huì)被 8 個(gè) ApplicationListener 監(jiān)聽(tīng)到。另外還有一個(gè) callRunners 方法值得注意,任何實(shí)現(xiàn)了 ApplicationRunner、CommandLineRunner 接口的實(shí)現(xiàn)類都會(huì)得到執(zhí)行。

9、加載 Bean(load)

前面提到過(guò) load 方法也是一個(gè)值得注意的方法,他可以通過(guò)好幾種方式注冊(cè) Bean:

private void load(Object source) {
Assert.notNull(source, "Source must not be null");
if (source instanceof Class<?>) {
load((Class<?>) source);
return;
}
if (source instanceof Resource) {
load((Resource) source);
return;
}
if (source instanceof Package) {
load((Package) source);
return;
}
if (source instanceof CharSequence) {
load((CharSequence) source);
return;
}
throw new IllegalArgumentException("Invalid source type " + source.getClass());
}

首先是 load(Class<?> source) 方法:它會(huì)判斷本地有沒(méi)有 groovy 環(huán)境,然后 source 對(duì)象是 GroovyBeanDefinitionSource 類或其子類的實(shí)例時(shí),就實(shí)例化它,然后將 loader 對(duì)象的 bean 方法返回的 Bean 添加到 groovyReader 中。然后就判斷其是否有資格注冊(cè)為 Bean。

private void load(Class<?> source) {
if (isGroovyPresent() && GroovyBeanDefinitionSource.class.isAssignableFrom(source)) {
// Any GroovyLoaders added in beans{} DSL can contribute beans here
GroovyBeanDefinitionSource loader = BeanUtils.instantiateClass(source, GroovyBeanDefinitionSource.class);
((GroovyBeanDefinitionReader) this.groovyReader).beans(loader.getBeans());
}
if (isEligible(source)) {
this.annotatedReader.register(source);
}
}

然后是 load(Resource source) 方法:總之它會(huì)從 grovvy 文件或者 xml 文件中注冊(cè) Bean。

private void load(Resource source) {
if (source.getFilename().endsWith(".groovy")) {
if (this.groovyReader == null) {
throw new BeanDefinitionStoreException("Cannot load Groovy beans without Groovy on classpath");
}
this.groovyReader.loadBeanDefinitions(source);
}
else {
if (this.xmlReader == null) {
throw new BeanDefinitionStoreException("Cannot load XML bean definitions when XML support is disabled");
}
this.xmlReader.loadBeanDefinitions(source);
}
}

然后是 load(Package source) 方法:總之它會(huì)從 package 里注冊(cè) Bean。

private void load(Package source) {
this.scanner.scan(source.getName());
}

最后就是 load(CharSequence source) 方法:它就很有意思了,它會(huì)嘗試將其作為以上三種方式進(jìn)行加載。

private void load(CharSequence source) {
String resolvedSource = this.scanner.getEnvironment().resolvePlaceholders(source.toString());
// Attempt as a Class
try {
load(ClassUtils.forName(resolvedSource, null));
return;
}
catch (IllegalArgumentException | ClassNotFoundException ex) {
// swallow exception and continue
}
// Attempt as Resources
if (loadAsResources(resolvedSource)) {
return;
}
// Attempt as package
Package packageResource = findPackage(resolvedSource);
if (packageResource != null) {
load(packageResource);
return;
}
throw new IllegalArgumentException("Invalid source '" + resolvedSource + "'");
}

10、總結(jié)

這篇文章我們了解了自動(dòng)裝配的工作方式,也就是 spring.factories。然后就是 Banner 是如何打印的、context 環(huán)境準(zhǔn)備完畢后如何執(zhí)行自定義代碼,context 的銷毀工作以及最后的兩種 runner 怎么使用。本文著重介紹了 load 方法通過(guò)幾種方式注冊(cè) Bean的,包括 Groovy、xml等文件方式、Package包、Class類、CharSequence字符串等方式進(jìn)行注冊(cè)。最后遺留了一個(gè)刷新上下文 refresh 方法沒(méi)有分析,這也是一個(gè)很重要的方法。

到此這篇關(guān)于SpringBoot 啟動(dòng)流程追蹤方法分享的文章就介紹到這了,更多相關(guān)SpringBoot 啟動(dòng)流程追蹤內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 解析java中的error該不該捕獲

    解析java中的error該不該捕獲

    這篇文章主要介紹了java中的error該不該捕獲,需要的朋友可以參考下
    2014-02-02
  • java原生動(dòng)態(tài)生成驗(yàn)證碼

    java原生動(dòng)態(tài)生成驗(yàn)證碼

    這篇文章主要為大家詳細(xì)介紹了java原生動(dòng)態(tài)生成驗(yàn)證碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-10-10
  • Java定時(shí)器Timer與TimerTask的使用詳解

    Java定時(shí)器Timer與TimerTask的使用詳解

    這篇文章主要介紹了Java定時(shí)器Timer與TimerTask的使用詳解,在JDK類庫(kù)中Timer主要負(fù)責(zé)計(jì)劃任務(wù)的功能,也就是在指定時(shí)間執(zhí)行某一任務(wù),執(zhí)行時(shí)候會(huì)在主線程之外起一個(gè)單獨(dú)的線程執(zhí)行指定的任務(wù),該類主要是設(shè)置任務(wù)計(jì)劃,但封裝的類是TimerTask類,需要的朋友可以參考下
    2023-10-10
  • Spring bean為什么默認(rèn)是單例

    Spring bean為什么默認(rèn)是單例

    這篇文章主要介紹了Spring bean為什么默認(rèn)是單例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Spring Security 表單登錄功能的實(shí)現(xiàn)方法

    Spring Security 表單登錄功能的實(shí)現(xiàn)方法

    這篇文章主要介紹了Spring Security 表單登錄,本文將構(gòu)建在之前簡(jiǎn)單的 Spring MVC示例 之上,因?yàn)檫@是設(shè)置Web應(yīng)用程序和登錄機(jī)制的必不可少的。需要的朋友可以參考下
    2019-06-06
  • 關(guān)于Spring Cloud健康檢查的陷阱

    關(guān)于Spring Cloud健康檢查的陷阱

    這篇文章主要介紹了關(guān)于Spring Cloud健康檢查的陷阱,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • MyBatis緩存功能原理及實(shí)例解析

    MyBatis緩存功能原理及實(shí)例解析

    這篇文章主要介紹了MyBatis緩存功能原理及實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • java初學(xué)者如何讓編程學(xué)習(xí)起來(lái)更簡(jiǎn)單

    java初學(xué)者如何讓編程學(xué)習(xí)起來(lái)更簡(jiǎn)單

    我們給大家?guī)?lái)一篇關(guān)于java初學(xué)者如何讓編程學(xué)習(xí)起來(lái)更簡(jiǎn)單的基礎(chǔ)性文章,有需要的朋友們可以學(xué)習(xí)下。
    2020-11-11
  • java遍歷途中修改數(shù)據(jù)及刪除數(shù)據(jù)的方法總結(jié)

    java遍歷途中修改數(shù)據(jù)及刪除數(shù)據(jù)的方法總結(jié)

    在使用java的集合類遍歷數(shù)據(jù)的時(shí)候,在某些情況下可能需要對(duì)某些數(shù)據(jù)進(jìn)行刪除,下面這篇文章主要給大家介紹了關(guān)于java遍歷途中修改數(shù)據(jù)及刪除數(shù)據(jù)的方法總結(jié),需要的朋友可以參考下
    2023-10-10
  • Springboot整合Mybatis和SQLite的詳細(xì)過(guò)程

    Springboot整合Mybatis和SQLite的詳細(xì)過(guò)程

    這篇文章主要介紹了Springboot整合Mybatis和SQLite的詳細(xì)過(guò)程,本文通過(guò)圖文示例相結(jié)合給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-07-07

最新評(píng)論