SpringBoot源码分析之SpringApplication构造方法核心源码分析

发布时间:2022-06-27 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了SpringBoot源码分析之SpringApplication构造方法核心源码分析脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  前面给大家介绍了SPRingBoot启动的核心流程,本文开始给大家详细的来介绍SpringBoot启动中的具体实现的相关细节

SpringBoot源码分析之SpringApplication构造方法核心源码分析

SpringApplication构造器

  首先我们来看下在SpringApplication的构造方法中是如何帮我们完成这4个核心操作的。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

	@SupPressWarnings({ "unchecked", "rawtyPEs" })
	public SpringApplication(ResourceLoader resourceLoader, Class<?>... PrimarySources) {
		// 传递的resourceLoader为null
		this.resourceLoader = resourceLoader;
		Assert.notNull(primarySources, "PrimarySources must not be null");
		// 记录主方法的配置类名称
		this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
		// 记录当前项目的类型
		this.webApplicationType = WebApplicationType.deduceFromClasspath();
		// 加载配置在spring.factories文件中的ApplicationContextInITializer对应的类型并实例化
		// 并将加载的数据存储在了 initializers 成员变量中。
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
		// 初始化监听器 并将加载的监听器实例对象存储在了listeners成员变量中
		setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
		// 反推main方法所在的Class对象 并记录在了mainApplicationClass对象中
		this.mainApplicationClass = deduceMainApplicationClass();
	}

1.webApplicationType

  首先来看下webApplicationType是如何来推导出当前启动的项目的类型。通过代码可以看到是通过deduceFromClassPath()方法根据ClassPath来推导出来的。

this.webApplicationType = WebApplicationType.deduceFromClasspath();

  跟踪进去看代码

	static WebApplicationType deduceFromClasspath() {
		if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
				&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
				&amp;& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
			return WebApplicationType.REACTIVE;
		}
		for (String classname : SERVLET_INDICATOR_CLASSES) {
			if (!ClassUtils.isPresent(className, null)) {
				return WebApplicationType.NONE;
			}
		}
		return WebApplicationType.SERVLET;
	}

  在看整体的实现逻辑之前,我们先分别看两个内容,第一就是在上面的代码中使用到了相关的静态变量。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  这些静态变量其实就是一些绑定的Java类的全类路径。第二个就是 ClassUtils.isPresent()方法,该方法的逻辑也非常简单,就是通过反射的方式获取对应的类型的Class对象,如果存在返回true,否则返回false

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  所以到此推导的逻辑就非常清楚了

SpringBoot源码分析之SpringApplication构造方法核心源码分析

2.setInitializers

  然后我们再来看下如何实现加载初始化器的。

// 加载配置在spring.factories文件中的ApplicationContextInitializer对应的类型并实例化
		// 并将加载的数据存储在了 initializers 成员变量中。
		setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

  首先所有的初始化器都实现了 ApplicationContextInitializer接口,也就是根据这个类型来加载相关的实现类。

public interface ApplicationContextInitializer<C extends configurableApplicationContext> {
    void initialize(C VAR1);
}

  然后加载的关键方法是 getSpringFactoriesInstances()方法。该方法会加载 spring.factories文件中的key为 org.springframework.context.ApplicationContextInitializer 的值。

spring-boot项目下

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,
org.springframework.boot.context.ContextIdApplicationContextInitializer,
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

spring-boot-autoconfigure项目下

# Initializers
org.springframework.context.ApplicationContextInitializer=
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,
org.springframework.boot.autoconfigure.LOGging.ConditionEvaluationReportLoggingListener

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  具体的加载方法为 getSpringFacotiesInstance()方法,我们进入查看

	private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
		// 获取当前上下文类加载器
		ClassLoader classLoader = getClassLoader();
		// 获取到的扩展类名存入set集合中止重复
		Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loaDFactoryNames(type, classLoader));
		// 创建扩展点实例
		List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
		AnnotationAwareOrderComparator.sort(instances);
		return instances;
	}

  先进入 SpringFactoriesLoader.loadFactoryNames(type, classLoader)中具体查看加载文件的过程.

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  然后我们来看下 loadSpringFactories方法

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  通过Debug的方式查看会更清楚哦

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  通过 loadSpringFactories 方法我们看到把 spring.factories文件中的所有信息都加载到了内存中了,但是我们现在只需要加载 ApplicationContextInitializer类型的数据。这时我们再通过 getOrDefault()方法来查看。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  进入方法中查看

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  然后会根据反射获取对应的实例对象。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  好了到这其实我们就清楚了 getSpringFactoriesInstances方法的作用就是帮我们获取定义在 META-iNF/spring.factories文件中的可以为 ApplicationContextInitializer 的值。并通过反射的方式获取实例对象。然后把实例的对象信息存储在了SpringApplication的 initializers属性中。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

3.setListeners

  清楚了 setInitializers()方法的作用后,再看 setListeners()方法就非常简单了,都是调用了 getSpringFactoriesInstances方法,只是传入的类型不同。也就是要获取的 META-INF/spring.factories文件中定义的不同信息罢了。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  即加载定义在 META-INF/spring.factories文件中声明的所有的监听器,并将获取后的监听器存储在了 SpringApplicationlisteners属性中。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  默认加载的监听器为F1a;

SpringBoot源码分析之SpringApplication构造方法核心源码分析

4.mainApplicationClass

  最后我们来看下 duduceMainApplicaitonClass()方法是如何反推导出main方法所在的Class对象的。通过码我们可以看到是通过 StackTrace来实现的。

StackTrace:
我们在学习函数调用时,都知道每个函数都拥有自己的栈空间。
一个函数被调用时,就创建一个新的栈空间。那么通过函数的嵌套调用最后就形成了一个函数调用堆栈

  StackTrace其实就是记录了程序方法执行的链路。通过Debug方式可以更直观的来呈现。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  那么相关的调用链路我们都可以获取到,剩下的就只需要获取每链路判断执行的方法名是否main就可以了。

SpringBoot源码分析之SpringApplication构造方法核心源码分析

  好了到此相关的4个核心步骤就给大家分析完了,希望对大家能有所帮助哦!

SpringBoot源码分析之SpringApplication构造方法核心源码分析

脚本宝典总结

以上是脚本宝典为你收集整理的SpringBoot源码分析之SpringApplication构造方法核心源码分析全部内容,希望文章能够帮你解决SpringBoot源码分析之SpringApplication构造方法核心源码分析所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。