SpringBoot启动时都做了哪些事(一)?

关联博文:
SpringBoot启动时都做了哪些事(一)?
SpringBoot启动时都做了哪些事(二)?
SpringBoot启动时都做了哪些事(三)?
SpringBoot启动时都做了哪些事(四)?

本文环境是SpringBoot 2.2.4.RELEASE,我们尝试去跟踪研究启动流程都做了哪些事情。

本文分析SpringApplication的实例化。核心有四点:

  • 确定primarySources 和主启动类
  • 确定webApplicationType
  • 检测并实例化ApplicationContextInitializer
  • 检测并实例化ApplicationListener

以main方法为入口。

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

SpringApplication的run方法。

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);
}

这里primarySource就是我们的主启动类,args默认为空。其分为SpringApplication的实例化,进而触发其run方法。

public SpringApplication(Class<?>... primarySources) {
	this(null, primarySources);
}

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();
	setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
	setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
	this.mainApplicationClass = deduceMainApplicationClass();
}

这里要特别注意的是setInitializers和setListeners。前者检索jar并实例化ApplicationContextInitializer,后者检索jar并实例化ApplicationListener然后赋予SpringApplication实例。

① getSpringFactoriesInstances

SpringApplication 的getSpringFactoriesInstances方法如下所示。

// SpringApplication 这里type为ApplicationContextInitializer.class
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
	return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
	ClassLoader classLoader = getClassLoader();
	// Use names and ensure unique to protect against duplicates
	
	// 检索符合目标类型的所有beanName
	Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
	
	//创建bean实例
	List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
	
	//对bean实例进行排序
	AnnotationAwareOrderComparator.sort(instances);

	//返回实例结果
	return instances;
}

② loadFactoryNames

将会从每个jar包下的META-INF/spring.factories路径扫描目标类型(factoryType),比如本文这里的ApplicationContextInitializer和ApplicationListener。

// SpringFactoriesLoader
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
	String factoryTypeName = factoryType.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

首先调用loadSpringFactories方法从每个jar的FACTORIES_RESOURCE_LOCATION==META-INF/spring.factories检索并获取配置信息放到MultiValueMap<String, String> result 中。

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

	try {
		Enumeration<URL> urls = (classLoader != null ?
				classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
				ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
		result = new LinkedMultiValueMap<>();
		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();
				for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
					result.add(factoryTypeName, factoryImplementationName.trim());
				}
			}
		}
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}

这里拿到的result如下所示,此时是一个全集,还没有进行过滤。
在这里插入图片描述
这个检索结果同时会往cache中放一下:

private static final Map<ClassLoader, MultiValueMap<String, String>> cache = 
new ConcurrentReferenceHashMap<>();

③ createSpringFactoriesInstances

遍历Set<String> names,反射进行实例化。

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
		ClassLoader classLoader, Object[] args, Set<String> names) {
	List<T> instances = new ArrayList<>(names.size());
	for (String name : names) {
		try {
			Class<?> instanceClass = ClassUtils.forName(name, classLoader);
			Assert.isAssignable(type, instanceClass);
			Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
			T instance = (T) BeanUtils.instantiateClass(constructor, args);
			instances.add(instance);
		}
		catch (Throwable ex) {
			throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
		}
	}
	return instances;
}

④ 实例结果

这里获取的ApplicationContextInitializer如下所示:

0 = "org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer"
1 = "org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener"
2 = "org.springframework.boot.devtools.restart.RestartScopeInitializer"
3 = "org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
4 = "org.springframework.boot.context.ContextIdApplicationContextInitializer"
5 = "org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
6 = "org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer"
7 = "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"

排序前后的instances(有8个):
在这里插入图片描述


获取的ApplicationListener如下(有13个):

0 = "org.springframework.boot.autoconfigure.BackgroundPreinitializer"
1 = "org.springframework.boot.devtools.restart.RestartApplicationListener"
2 = "org.springframework.boot.devtools.logger.DevToolsLogFactory.Listener"
3 = "org.springframework.boot.ClearCachesApplicationListener"
4 = "org.springframework.boot.builder.ParentContextCloserApplicationListener"
5 = "org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor"
6 = "org.springframework.boot.context.FileEncodingApplicationListener"
7 = "org.springframework.boot.context.config.AnsiOutputApplicationListener"
8 = "org.springframework.boot.context.config.ConfigFileApplicationListener"
9 = "org.springframework.boot.context.config.DelegatingApplicationListener"
10 = "org.springframework.boot.context.logging.ClasspathLoggingApplicationListener"
11 = "org.springframework.boot.context.logging.LoggingApplicationListener"
12 = "org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener"

排序后的ApplicationListener:

0 = {RestartApplicationListener@1940} 
1 = {CloudFoundryVcapEnvironmentPostProcessor@1941} 
2 = {ConfigFileApplicationListener@1942} 
3 = {AnsiOutputApplicationListener@1943} 
4 = {LoggingApplicationListener@1944} 
5 = {BackgroundPreinitializer@1945} 
6 = {ClasspathLoggingApplicationListener@1946} 
7 = {DelegatingApplicationListener@1947} 
8 = {ParentContextCloserApplicationListener@1948} 
9 = {DevToolsLogFactory$Listener@1949} 
10 = {ClearCachesApplicationListener@1950} 
11 = {FileEncodingApplicationListener@1951} 
12 = {LiquibaseServiceLocatorApplicationListener@1952} 

接下来就会触发SpringApplication实例的run方法了。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值