阿志@Duan

越努力,越幸福!
SpringBoot启动及配置文件加载原理分析

大家好,这篇文章将跟大家一起来分析一下SpringBoot启动及配置文件加载原理分析。废话不多说,直接开干吧。。。。。

一、看前必备技能

        Spring SPI机制,类似像Java的ServiceLoader、或者Dubbo的ExtensionLoader。

        因为下面讲到的Spring的Listener加载机制会涉及到SPI技术(当然这里Spring的监听事件不清楚的小伙伴可以先去做个了解)。

二、SpringBoot初始化启动原理分析

        1、由启动主动函数入口切入分析

        

 

       2、由启动源码分析 SpringApplication.run(DszMasterApplication.class, args);这里面有两个内容,第一初始化SpringApplication,第二是初始化SpringApplication后开始真正运行run方法。

      如下图:

               

 

 

        3、这里先看SpringApplication的初始化,其实里面最重要的就是SpringBoot自动装载机制,像监听器Listeners初始化,还有什么InitiaLizers。当然还有很多其他初始化任务。这里我们来重点看一下Listener监听注册的初始化。

            由上面截图new SpringApplication()构造方法进去可以看到

                

 

 

              这里我们重点看下setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));,由getSpringFactoriesInstances进去可以看到

               

 

 

             这里的names就是收集了初始化后的ApplicationListener的所有实现的监听器,具体有10个如下图

              

 

 

              接下来给大家再往下具体分析这个监听器是如何被初始化的,进入SpringFactoriesLoader.loadFactoryNames(type, classLoader)中如下

              

 

 

                然后再进入loadSpringFactories方法,如下

                       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 ?
//下面的的 FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”,这个地方往下就会涉及到Spring的自动装配SPI机制了
             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 factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
//这里将会吧所有的初始化的监听对象放入缓存中
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}

      那么上面的spring.factories文件是在哪里的呢,通过调试得知如下图

                        

 

 

                      由此可知我们可以按照这个path找出这个文件

                          

 

 

                         现在让我们来看下这个文件到底张什么样的,我们打开这个文章一起来看下

                         

            # PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers

# 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.web.context.ServerPortInfoApplicationContextInitializer

# Application Listeners
org.springframework.context.ApplicationListener=\
//以下都是ApplicatinListener的实现类,这些类都将会在上面
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor

# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ConnectorStartFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\
org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer

# FailureAnalysisReporters
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter

         由上内容得知,spring.factories定义了一系列springboot启动需要杯加载初始化的类,通过如下调试得知

                            

 

                           这里我们来具体看下ApplictionListener加载的那些类,因为这些跟我们接下来要讲的CnfigFileListener会有关系。其实很简单,在MultiValueMap<String, String> result = cache.get(classLoader);的结果集别刻意看出,因为最终初始化的

                          所有对象都将add到这个result中去。看下结果图如下: 

                           

 

                           在这里边可以看到所有的Listener初始化的结果。

                            今晚由于时间比较晚,还有run部分和配置文件加载原理就留给下次给大家补上。由于最近刚写博客,样式排版上没有太多精力,后期会给大家慢慢优化回来。

                           今晚总结时序图给大家回顾一下吧:

                            

 

                           具体细节大家按照上面讲的回顾就好了。

 

                      

                         

 

                           

 

 

 

    

                 

 

posted on 2019-09-12 17:20  Dsz.java  阅读(5320)  评论(0编辑  收藏  举报