本文是基于springboot2.1.6源码解析
本篇主要是介绍,spring怎么加载解析配置文件到environment中。至于怎么获取environment中配置,怎么绑定environment到对象中,另开篇再介绍。不想看源码,直接看总结
目录
1. SpringApplication run基本介绍
SpringApplication的run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
//1 获取SpringApplicationRunListener,从spring.factories中自动装配,获取到的listener是EventPublishingRunListener,实例化EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//2 装载environment
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
1.1 SpringApplicationRunListener介绍(上述代码序号1解释)
- 获取SpringApplicationRunListener,从spring.factories中自动装配,项目中只有spring-boot.2.1.6.RELEASE包中有配置
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
- 实例化EventPublishingRunListener。如下EventPublishingRunListener构造函数
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
//把SpringApplication中,当前应用中所有的listener都添加到需要监听广播的列表中。一旦有事件发布,所有的listener都能监听到相应的时间
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
SpringApplication中的listener有如下(此处是一个拓展点,可以实现自己的listener,监听相应的事件),此处也是自动装配,在SpringApplication构造函数中(暂不详细介绍)
# Application Listeners
org.springframework.context.ApplicationListener=\
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
2. 装载Environment(上述代码序号2解释)
SpringApplication的prepareEnvironment方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//2.1 Create and configure the environment 创建和配置environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
//2.2 加载启动命令行配置属性,active属性。(先不深入讲解)
configureEnvironment(environment, applicationArguments.getSourceArgs());
//2.3 发布事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
//此处把上述解析的所有配置文件PropertySources,添加到key为configurationProperties的PropertySource对象中,并添加到PropertySources的第一个元素中(先不深入,后续查询配置篇再讲)
ConfigurationPropertySources.attach(environment);
return environment;
}
private ConfigurableEnvironment getOrCreateEnvironment() {
//此处可以设置自定义environment,此处是一个拓展点(先不深入讲解)
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
//spring boot应用创建的是StandardServletEnvironment
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
2.1 创建和配置environment
-
实例化StandardServletEnvironment,我们可以看一下StandardServletEnvironment的继承依赖关系。实例化StandardServletEnvironment必定会实例化其所有父类。此处我们重点关注AbstractEnvironment。
-
AbstractEnvironment实例化过程中,构造方法会被调用。
public AbstractEnvironment() {
customizePropertySources(this.propertySources);
}
//加载自定义配置文件,如果没有自定义,我们实例化的是StandardServletEnvironment。此处是一个拓展点
protected void customizePropertySources(MutablePropertySources propertySources) {
}
- StandardServletEnvironment类customizePropertySources,把配置添加到propertySources
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
propertySources.addLast(new StubPropertySource("servletContextInitParams"));
if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
propertySources.addLast(new JndiPropertySource("jndiProperties"));
}
//调用父类StandardEnvironment的customizePropertySources
super.customizePropertySources(propertySources);
}
- StandardEnvironment的customizePropertySources。(此处的systemEnvironment, systemProperties为JVM系统属性 和系统环境变量)
/** System environment property source name: {@value}. */
public static final String SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME = "systemEnvironment";
/** JVM system properties property source name: {@value}. */
public static final String SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME = "systemProperties";
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
propertySources.addLast(
new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
propertySources.addLast(
new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
}
-
ConfigurableEnvironment environment = getOrCreateEnvironment();执行完之后,debug如下
2.2 加载启动命令行配置属性,active属性。(先不深入讲解)
2.3 发布事件
-
SpringApplicationRunListeners
public void environmentPrepared(ConfigurableEnvironment environment) { //遍历所有的listener,发布事件。我们前面知道是SpringApplicationRunListeners实际实例化子类是EventPublishingRunListener for (SpringApplicationRunListener listener : this.listeners) { listener.environmentPrepared(environment); } }
-
EventPublishingRunListener
public void environmentPrepared(ConfigurableEnvironment environment) { //发布ApplicationEnvironmentPreparedEvent事件(即加载配置文件) this.initialMulticaster .multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment)); } //此处即观察者模式,一直跟进去到,最终调用的是listener.onApplicationEvent(event);此处为通知加载配置文件 public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) { ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event)); Executor executor = getTaskExecutor(); for (ApplicationListener<?> listener : getApplicationListeners(event, type)) { if (executor != null) { executor.execute(() -> invokeListener(listener, event)); } else { invokeListener(listener, event); } } }
虽然有很多listener,但是监听处理ApplicationEnvironmentPreparedEvent事件的只有ConfigFileApplicationListener
-
ConfigFileApplicationListener
@Override public void onApplicationEvent(ApplicationEvent event) { //监听的时间属于ApplicationEnvironmentPreparedEvent,则处理 if (event instanceof ApplicationEnvironmentPreparedEvent) { onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event); } if (event instanceof ApplicationPreparedEvent) { onApplicationPreparedEvent(event); } } private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) { //此处从spring.factories中查找所有EnvironmentPostProcessor,(此处是一个拓展点,可以自定义自己的EnvironmentPostProcessor,加载自定义配置文件到environment中) List<EnvironmentPostProcessor> postProcessors = loadPostProcessors(); postProcessors.add(this); AnnotationAwareOrderComparator.sort(postProcessors); //执行postProcessors的postProcessEnvironment,此处调试有多个,我们先关注ConfigFileApplicationListener这个(即本类,可以看到它实现了EnvironmentPostProcessor接口) for (EnvironmentPostProcessor postProcessor : postProcessors) { postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()); } } List<EnvironmentPostProcessor> loadPostProcessors() { return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader()); } @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { addPropertySources(environment, application.getResourceLoader()); } protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { RandomValuePropertySource.addToEnvironment(environment); //上面没有注释的按顺序调用,我们直接更进去此处就好,此Loader为一个内部类 new Loader(environment, resourceLoader).load(); }
-
ConfigFileApplicationListener$Loader
//构造函数 Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { this.environment = environment; this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment); this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(); //此处从spring.factories中获取PropertySourceLoader的实现类,我们可以看到只有两个,如下代码PropertiesPropertySourceLoader,YamlPropertySourceLoader this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader()); }
# PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ org.springframework.boot.env.YamlPropertySourceLoader
-
Loader类load方法
public void load() { this.profiles = new LinkedList<>(); this.processedProfiles = new LinkedList<>(); this.activatedProfiles = false; this.loaded = new LinkedHashMap<>(); //初始化profiles环境信息,profiles。 //首先添加一个null元素,如果没有设置环境,再添加一个default。目的是为了后续解析application.yml或application-default.yml initializeProfiles(); while (!this.profiles.isEmpty()) { Profile profile = this.profiles.poll(); if (profile != null && !profile.isDefaultProfile()) { addProfileToEnvironment(profile.getName()); } //首先执行profile=null。同时我们特别注意第三个参数addToLoaded(MutablePropertySources::addLast, false),该参数是一个函数,最后解析完文件会执行,我们往下看即可 load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false)); this.processedProfiles.add(profile); } resetEnvironmentProfiles(this.processedProfiles); load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true)); //把上面加载的配置(即loaded中的PropertySource,设置到environment中的PropertySources中),最后面解析 addLoadedPropertySources(); }
-
Loader类load方法(上述方法跟进去)
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { //getSearchLocations()获取需要加载配置文件的目录 //默认是四个目录"classpath:/,classpath:/config/,file:./,file:./config/" getSearchLocations().forEach((location) -> { boolean isFolder = location.endsWith("/"); //getSearchNames()获取文件的名称,如果没有配置,默认application Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES; names.forEach((name) -> load(location, name, profile, filterFactory, consumer)); }); }
-
上述load方法跟进去,调过了两个方法,来到最终的load方法
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) { try { //加载location对应的文件(location为文件全路径。例:file:./config/application.properties) Resource resource = this.resourceLoader.getResource(location); //...代码省略 String name = "applicationConfig: [" + location + "]"; //用对应的PropertiesPropertySourceLoader或YamlPropertySourceLoader加载对应配置文件的key,value List<Document> documents = loadDocuments(loader, name, resource); if (CollectionUtils.isEmpty(documents)) { if (this.logger.isTraceEnabled()) { StringBuilder description = getDescription("Skipped unloaded config ", location, resource, profile); this.logger.trace(description); } return; } List<Document> loaded = new ArrayList<>(); for (Document document : documents) { if (filter.match(document)) { addActiveProfiles(document.getActiveProfiles()); addIncludedProfiles(document.getIncludeProfiles()); loaded.add(document); } } Collections.reverse(loaded); if (!loaded.isEmpty()) { //注意注意:此处我们会调用第一层load进来时传进来的函数式参数,即addToLoaded(MutablePropertySources::addLast, false) loaded.forEach((document) -> consumer.accept(profile, document)); if (this.logger.isDebugEnabled()) { StringBuilder description = getDescription("Loaded config file ", location, resource, profile); this.logger.debug(description); } } } catch (Exception ex) { throw new IllegalStateException("Failed to load property " + "source from location '" + location + "'", ex); } }
-
ConfigFileApplicationListener addToLoaded(MutablePropertySources::addLast, false)
//从调用点看,此处 addMethod也是一个函数MutablePropertySources::addLast(添加到最后面) private DocumentConsumer addToLoaded(BiConsumer<MutablePropertySources, PropertySource<?>> addMethod, boolean checkForExisting) { return (profile, document) -> { if (checkForExisting) { for (MutablePropertySources merged : this.loaded.values()) { if (merged.contains(document.getPropertySource().getName())) { return; } } } //从当前loaded中获取profile为key的MutablePropertySources,如果没找到,则新建一个 MutablePropertySources merged = this.loaded.computeIfAbsent(profile, (k) -> new MutablePropertySources()); //把解析出来的配置,添加到MutablePropertySources中最后一个,最终配置会添加到loaded中,debug截图如下 addMethod.accept(merged, document.getPropertySource()); }; }
-
回到3.5中addLoadedPropertySources(),此处会把load加载的配置文件生成的PropertySource添加到environment中(此处可以看到是按什么顺序添加)
private void addLoadedPropertySources() { MutablePropertySources destination = this.environment.getPropertySources(); List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values()); Collections.reverse(loaded); String lastAdded = null; Set<String> added = new HashSet<>(); for (MutablePropertySources sources : loaded) { for (PropertySource<?> source : sources) { if (added.add(source.getName())) { addLoadedPropertySource(destination, lastAdded, source); lastAdded = source.getName(); } } } }
3. 总结
上面都没看懂,没关系,我们总结一下:
-
PropertySources是一个存储配置文件的存储单元,该接口默认实现为MutablePropertySources,里面的proertySourceList就是保存的所有配置文件。
-
proertySourceList保存的数据如下图:(我们看到从上到下有系统配置,环境变量配置,application配置)。proertySourceList存储的对象是PropertySource。PropertySource中name即为文件名或某一特定的配置标识,source就是该文件对应的key/value
-
我们的environment,默认实现的类是AbstractEnvironment,所有environment实现都会继承AbstractEnvironment。我们可以看下面类图,AbstractEnvironment中就包含了上述MutablePropertySources(即存储配置文件的对象),所以这就是为什么从environment中可以获取所有配置的原因了。
-
ConfigFileApplicationListener作用就是监听事件,加载解析配置文件,设置到environment中
-
但为什么environment要继承PropertyResolver呢?获取配置的时候有用,这个我们一直没讲。SpringBoot源码解析:Environment获取配置原理(二)。
-
environment中的配置又是怎么通过@Value和@configurationProperties绑定到对象中呢?。后续介绍。
-
回顾一下以上源码:就是springapplication自动装配ApplicationListener(ConfigFileApplicationListener),利用观察者模式,监听ApplicationEvent。SpringApplication创建好了environment之后,发布ApplicationEnvironmentPreparedEvent事件。ConfigFileApplicationListener监听到此事件之后,加载配置文件,设置到environment中
-
上述有很多拓展点,都已标记,可以重点关注,以便我们自定义开发。