1.使用getEnvironment()获取环境信息
ApplicationContext接口继承了EnvironmentCapable接口,可以通过getEnvironment()
获取Environment配置信息,例如:
java
@SpringBootApplication
public class A01 {
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext context = SpringApplication.run(A01.class, args);
// 获取系统配置及自定义配置信息
System.out.println(context.getEnvironment().getProperty("java_home"));
System.out.println(context.getEnvironment().getProperty("server.port"));
}
}
接下来我们就探究下SpringBoot是如何加载环境及配置信息的
2.Spring读取环境信息原理
2.1 创建Environment对象
SpringBoot启动时就会调用prepareEnvironment
创建Environment对象,SpringApplication#run源码如下:
java
public ConfigurableApplicationContext run(String... args) {
// 省略其他代码...
// 创建Environment对象,其中包括系统环境信息及Spring配置文件信息
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 将一些对象设置到容器中,其中包含Environment对象
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
// 省略其他代码...
return context;
}
进入prepareEnvironment方法中创建Environment对象的源码如下:
java
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// 1.创建ConfigurableEnvironment对象
ConfigurableEnvironment environment = this.getOrCreateEnvironment();
// 补充启动参数
this.configureEnvironment(environment, applicationArguments.getSourceArgs());
// 创建ConfigurationPropertySources
ConfigurationPropertySources.attach(environment);
// 2.通过发送事件的方式加载Spring配置
listeners.environmentPrepared(bootstrapContext, environment);
// 将DefaultPropertiesPropertySource移动到数组尾部
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties.");
this.bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
EnvironmentConverter environmentConverter = new EnvironmentConverter(this.getClassLoader());
environment = environmentConverter.convertEnvironmentIfNecessary(environment, this.deduceEnvironmentClass());
}
// 更新ConfigurationPropertySources
ConfigurationPropertySources.attach(environment);
return environment;
}
创建ConfigurableEnvironment对象源码如下:
java
private ConfigurableEnvironment getOrCreateEnvironment() {
// 此时environment为null
if (this.environment != null) {
return this.environment;
} else {
// 调用DefaultApplicationContextFactory创建environment
ConfigurableEnvironment environment = this.applicationContextFactory.createEnvironment(this.webApplicationType);
if (environment == null && this.applicationContextFactory != ApplicationContextFactory.DEFAULT) {
environment = ApplicationContextFactory.DEFAULT.createEnvironment(this.webApplicationType);
}
return (ConfigurableEnvironment)(environment != null ? environment : new ApplicationEnvironment());
}
}
// DefaultApplicationContextFactory#createEnvironment
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
// 调用静态方法引用ApplicationContextFactory::createEnvironment创建environment
return (ConfigurableEnvironment)this.getFromSpringFactories(webApplicationType, ApplicationContextFactory::createEnvironment, (Supplier)null);
}
private <T> T getFromSpringFactories(WebApplicationType webApplicationType, BiFunction<ApplicationContextFactory, WebApplicationType, T> action, Supplier<T> defaultResult) {
Iterator var4 = SpringFactoriesLoader.loadFactories(ApplicationContextFactory.class, this.getClass().getClassLoader()).iterator();
Object result;
do {
if (!var4.hasNext()) {
return defaultResult != null ? defaultResult.get() : null;
}
ApplicationContextFactory candidate = (ApplicationContextFactory)var4.next();
// 创建environment,实际调用的是Factory#createEnvironment
result = action.apply(candidate, webApplicationType);
} while(result == null);
return result;
}
这里的静态方法引用调用的是AnnotationConfigServletWebServerApplicationContext.Factory#createEnvironment,创建的对象是ApplicationServletEnvironment的实例
java
public ConfigurableEnvironment createEnvironment(WebApplicationType webApplicationType) {
return webApplicationType != WebApplicationType.SERVLET ? null : new ApplicationServletEnvironment();
}
2.2 读取系统配置
ApplicationServletEnvironment创建实例时会调用父类AbstractEnvironment的构造函数初始化环境信息
java
public AbstractEnvironment() {
this(new MutablePropertySources());
}
protected AbstractEnvironment(MutablePropertySources propertySources) {
this.logger = LogFactory.getLog(this.getClass());
this.activeProfiles = new LinkedHashSet();
this.defaultProfiles = new LinkedHashSet(this.getReservedDefaultProfiles());
this.propertySources = propertySources;
this.propertyResolver = this.createPropertyResolver(propertySources);
// 会调用到StandardEnvironment#customizePropertySources加载系统配置
this.customizePropertySources(propertySources);
}
调用子类的StandardEnvironment#customizePropertySources加载系统配置
java
protected void customizePropertySources(MutablePropertySources propertySources) {
// 读取系统配置
propertySources.addLast(new PropertiesPropertySource("systemProperties", this.getSystemProperties()));
// 读取系统信息
propertySources.addLast(new SystemEnvironmentPropertySource("systemEnvironment", this.getSystemEnvironment()));
}
// AbstractEnvironment#getSystemProperties
public Map<String, Object> getSystemProperties() {
try {
// 获取系统配置
return System.getProperties();
} catch (AccessControlException var2) {
return new ReadOnlySystemAttributesMap() {
@Nullable
protected String getSystemAttribute(String attributeName) {
try {
return System.getProperty(attributeName);
} catch (AccessControlException var3) {
if (AbstractEnvironment.this.logger.isInfoEnabled()) {
AbstractEnvironment.this.logger.info("Caught AccessControlException when accessing system property '" + attributeName + "'; its value will be returned [null]. Reason: " + var3.getMessage());
}
return null;
}
}
};
}
}
// AbstractEnvironment#getSystemEnvironment
public Map<String, Object> getSystemEnvironment() {
if (this.suppressGetenvAccess()) {
return Collections.emptyMap();
} else {
try {
// 读取环境信息
return System.getenv();
} catch (AccessControlException var2) {
return new ReadOnlySystemAttributesMap() {
@Nullable
protected String getSystemAttribute(String attributeName) {
try {
return System.getenv(attributeName);
} catch (AccessControlException var3) {
if (AbstractEnvironment.this.logger.isInfoEnabled()) {
AbstractEnvironment.this.logger.info("Caught AccessControlException when accessing system environment variable '" + attributeName + "'; its value will be returned [null]. Reason: " + var3.getMessage());
}
return null;
}
}
};
}
}
}
到这一步,系统的环境信息就获取到了,例如java.home、os.version等,那么Spring配置文件application.properties等配置信息怎么获取的呢?
2.3 加载Spring配置
2.3.1 发布事件
加载Spring配置是通过调用listeners.environmentPrepared(bootstrapContext, environment)发布ApplicationEnvironmentPreparedEvent事件来完成的,具体看源码SpringApplicationRunListeners#environmentPrepared
java
void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
this.doWithListeners("spring.boot.application.environment-prepared", (listener) -> {
listener.environmentPrepared(bootstrapContext, environment);
});
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
this.doWithListeners(stepName, listenerAction, (Consumer)null);
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
// 调用消费者发布事件
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
doWithListeners中会调用listener.environmentPrepared(bootstrapContext, environment)发布事件,源码见EventPublishingRunListener#environmentPrepared
java
public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(bootstrapContext, this.application, this.args, environment));
}
可见发布的事件类型是ApplicationEnvironmentPreparedEvent,进入multicastEvent方法
java
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);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
// 省略异常处理代码...
}
最终调用ApplicationListener的onApplicationEvent方法处理事件的
2.3.2 注册事件处理器
这里我们可能疑惑,这些事件处理器是怎么注册的呢?
首先SpringBoot项目启动时,会调用SpringApplication构造器设置listeners
java
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));
// 设置ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
进入getSpringFactoriesInstances(ApplicationListener.class)方法内部
java
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();
// 从配置文件中获取listener类名
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
通过读取spring.factories配置文件获取listener类名,然后实例化对象
java
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
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 {
// 获取META-INF/spring.factories资源文件URL
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());
}
}
}
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;
}
spring-boot的jar包中的META-INF/spring.factories配置如下:
# 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.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
所以EnvironmentPostProcessorApplicationListener会被添加到容器中
当调用SpringApplication.run时,EventPublishingRunListener也会被创建,源码如下:
java
public ConfigurableApplicationContext run(String... args) {
// 省略其他代码...
// 创建EventPublishingRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
// 省略其他代码...
}
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
this.applicationStartup);
}
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();
// 从配置文件中获取SpringApplicationRunListener的类名
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化对象
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
spring-boot的jar包中的META-INF/spring.factories配置如下:
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
所以EventPublishingRunListener对象会被创建,源码如下:
java
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// 添加ApplicationListener到initialMulticaster中
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
application.getListeners()会获取到上文中添加到容器中的listeners,将每个listener添加到initialMulticaster中,源码如下:
java
public void addApplicationListener(ApplicationListener<?> listener) {
synchronized (this.defaultRetriever) {
Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
if (singletonTarget instanceof ApplicationListener) {
this.defaultRetriever.applicationListeners.remove(singletonTarget);
}
// 添加到defaultRetriever的applicationListeners成员变量中
this.defaultRetriever.applicationListeners.add(listener);
this.retrieverCache.clear();
}
}
这样defaultRetriever的applicationListeners成员变量就保存了这些listener对象
再看看multicastEvent方法中的getApplicationListeners(event, type),会调用retrieveApplicationListeners
java
private Collection<ApplicationListener<?>> retrieveApplicationListeners(
ResolvableType eventType, @Nullable Class<?> sourceType, @Nullable CachedListenerRetriever retriever) {
List<ApplicationListener<?>> allListeners = new ArrayList<>();
Set<ApplicationListener<?>> filteredListeners = (retriever != null ? new LinkedHashSet<>() : null);
Set<String> filteredListenerBeans = (retriever != null ? new LinkedHashSet<>() : null);
Set<ApplicationListener<?>> listeners;
Set<String> listenerBeans;
synchronized (this.defaultRetriever) {
// 从defaultRetriever中获取listeners
listeners = new LinkedHashSet<>(this.defaultRetriever.applicationListeners);
listenerBeans = new LinkedHashSet<>(this.defaultRetriever.applicationListenerBeans);
}
// 过滤符合条件的listener
for (ApplicationListener<?> listener : listeners) {
if (supportsEvent(listener, eventType, sourceType)) {
if (retriever != null) {
filteredListeners.add(listener);
}
allListeners.add(listener);
}
}
// 省略部分代码...
return allListeners;
}
这样就获取到了listener处理事件,其中就包括EnvironmentPostProcessorApplicationListener
2.3.3 处理事件
EnvironmentPostProcessorApplicationListener#onApplicationEvent会处理上述发布的ApplicationEnvironmentPreparedEvent事件
java
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
// 处理ApplicationEnvironmentPreparedEvent事件
this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);
}
if (event instanceof ApplicationPreparedEvent) {
this.onApplicationPreparedEvent();
}
if (event instanceof ApplicationFailedEvent) {
this.onApplicationFailedEvent();
}
}
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
// 获取环境相关的后处理器
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
postProcessor.postProcessEnvironment(environment, application);
}
}
getEnvironmentPostProcessors会读取spring.factories配置文件获取EnvironmentPostProcessor
java
List<EnvironmentPostProcessor> getEnvironmentPostProcessors(ResourceLoader resourceLoader,
ConfigurableBootstrapContext bootstrapContext) {
ClassLoader classLoader = (resourceLoader != null) ? resourceLoader.getClassLoader() : null;
// 这个postProcessorsFactory是通过构造器添加的
EnvironmentPostProcessorsFactory postProcessorsFactory = this.postProcessorsFactory.apply(classLoader);
return postProcessorsFactory.getEnvironmentPostProcessors(this.deferredLogs, bootstrapContext);
}
this.postProcessorsFactory.apply(classLoader)会调用如下EnvironmentPostProcessorsFactory#fromSpringFactories,加载EnvironmentPostProcessor
java
static EnvironmentPostProcessorsFactory fromSpringFactories(ClassLoader classLoader) {
return new ReflectionEnvironmentPostProcessorsFactory(classLoader,
SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, classLoader));
}
spring-boot的jar包中的META-INF/spring.factories配置如下:
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\
org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\
org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\
org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\
org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor
getEnvironmentPostProcessors就会实例化EnvironmentPostProcessor,这样ConfigDataEnvironmentPostProcessor对象就会被创建,调用它的postProcessEnvironment时获取配置信息
java
void postProcessEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
Collection<String> additionalProfiles) {
try {
this.logger.trace("Post-processing environment to add config data");
resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader();
// 创建ConfigDataEnvironment获取配置信息
getConfigDataEnvironment(environment, resourceLoader, additionalProfiles).processAndApply();
}
catch (UseLegacyConfigProcessingException ex) {
this.logger.debug(LogMessage.format("Switching to legacy config file processing [%s]",
ex.getConfigurationProperty()));
configureAdditionalProfiles(environment, additionalProfiles);
postProcessUsingLegacyApplicationListener(environment, resourceLoader);
}
}
ConfigDataEnvironment getConfigDataEnvironment(ConfigurableEnvironment environment, ResourceLoader resourceLoader,
Collection<String> additionalProfiles) {
return new ConfigDataEnvironment(this.logFactory, this.bootstrapContext, environment, resourceLoader,
additionalProfiles, this.environmentUpdateListener);
}
ConfigDataEnvironment是SpringBoot配置数据加载和管理的核心组件,它的构造函数如下,主要创建了ConfigDataLocationResolvers、ConfigDataLoaders及ConfigDataEnvironmentContributors
java
ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection<String> additionalProfiles,
ConfigDataEnvironmentUpdateListener environmentUpdateListener) {
Binder binder = Binder.get(environment);
UseLegacyConfigProcessingException.throwIfRequested(binder);
this.logFactory = logFactory;
this.logger = logFactory.getLog(getClass());
this.notFoundAction = binder.bind(ON_NOT_FOUND_PROPERTY, ConfigDataNotFoundAction.class)
.orElse(ConfigDataNotFoundAction.FAIL);
this.bootstrapContext = bootstrapContext;
this.environment = environment;
// 创建配置文件位置解析器
this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader);
this.additionalProfiles = additionalProfiles;
this.environmentUpdateListener = (environmentUpdateListener != null) ? environmentUpdateListener
: ConfigDataEnvironmentUpdateListener.NONE;
// 创建配置文件加载器
this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext, resourceLoader.getClassLoader());
// 创建ConfigDataEnvironmentContributors
this.contributors = createContributors(binder);
}
// ConfigDataLocationResolvers构造器
ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
Binder binder, ResourceLoader resourceLoader) {
this(logFactory, bootstrapContext, binder, resourceLoader, SpringFactoriesLoader
.loadFactoryNames(ConfigDataLocationResolver.class, resourceLoader.getClassLoader()));
}
// ConfigDataLoaders构造器
ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
ClassLoader classLoader) {
this(logFactory, bootstrapContext, classLoader,
SpringFactoriesLoader.loadFactoryNames(ConfigDataLoader.class, classLoader));
}
这里也通过SpringFactoriesLoader#loadFactoryNames从配置文件中创建ConfigDataLocationResolver及ConfigDataLoader的,spring-boot的jar包中的META-INF/spring.factories配置如下:
# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
org.springframework.boot.context.config.StandardConfigDataLocationResolver
# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
org.springframework.boot.context.config.StandardConfigDataLoader
其中StandardConfigDataLocationResolver用于解析标准的配置文件位置,例如类路径下的config目录中的applicaiton.properties或者applicaiton.yml,而StandardConfigDataLoader则用于加载标准的配置数据
创建ConfigDataEnvironmentContributors源码如下:
java
private ConfigDataEnvironmentContributors createContributors(Binder binder) {
this.logger.trace("Building config data environment contributors");
MutablePropertySources propertySources = this.environment.getPropertySources();
List<ConfigDataEnvironmentContributor> contributors = new ArrayList<>(propertySources.size() + 10);
PropertySource<?> defaultPropertySource = null;
for (PropertySource<?> propertySource : propertySources) {
if (DefaultPropertiesPropertySource.hasMatchingName(propertySource)) {
defaultPropertySource = propertySource;
}
else {
this.logger.trace(LogMessage.format("Creating wrapped config data contributor for '%s'",
propertySource.getName()));
contributors.add(ConfigDataEnvironmentContributor.ofExisting(propertySource));
}
}
// 关键方法:getInitialImportContributors
contributors.addAll(getInitialImportContributors(binder));
if (defaultPropertySource != null) {
this.logger.trace("Creating wrapped config data contributor for default property source");
contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource));
}
return createContributors(contributors);
}
protected ConfigDataEnvironmentContributors createContributors(
List<ConfigDataEnvironmentContributor> contributors) {
return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapContext, contributors);
}
ConfigDataEnvironmentContributors主要作用是维护一组ConfigDataEnvironmentContributor,而每个ConfigDataEnvironmentContributor用于配置数据的加载、处理和管理
这里重点看下getInitialImportContributors(binder)获取初始导入的ConfigDataEnvironmentContributor,这决定了配置文件的加载位置及顺序
java
private List<ConfigDataEnvironmentContributor> getInitialImportContributors(Binder binder) {
List<ConfigDataEnvironmentContributor> initialContributors = new ArrayList<>();
// 1.指定spring.config.import要导入的额外配置文件位置
addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS));
// 2.指定spring.config.additional-location额外的配置文件位置
addInitialImportContributors(initialContributors,
bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS));
// 3.指定spring.config.location默认配置文件位置
addInitialImportContributors(initialContributors,
bindLocations(binder, LOCATION_PROPERTY, DEFAULT_SEARCH_LOCATIONS));
return initialContributors;
}
private void addInitialImportContributors(List<ConfigDataEnvironmentContributor> initialContributors,
ConfigDataLocation[] locations) {
// 从后往前遍历
for (int i = locations.length - 1; i >= 0; i--) {
initialContributors.add(createInitialImportContributor(locations[i]));
}
}
在第3步中指定的配置文件地址DEFAULT_SEARCH_LOCATIONS包含:
java
static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
static {
List<ConfigDataLocation> locations = new ArrayList<>();
locations.add(ConfigDataLocation.of("optional:classpath:/;optional:classpath:/config/"));
locations.add(ConfigDataLocation.of("optional:file:./;optional:file:./config/;optional:file:./config/*/"));
DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
}
而addInitialImportContributors是从后往前遍历的,因此加载顺序为:
file:./
file:./config/
file:./config/*/
classpath:/
classpath:/config/
创建ConfigDataEnvironmentContributors完成后就会调用ConfigDataEnvironment#processAndApply加载和解析配置文件,源码如下:
java
void processAndApply() {
// 创建数据导入器
ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
this.loaders);
registerBootstrapBinder(this.contributors, null, DENY_INACTIVE_BINDING);
// 执行加载以及解析初始的配置文件
ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
// 处理profiles相关的配置,包括云平台配置及本地环境配置
ConfigDataActivationContext activationContext = createActivationContext(
contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE));
contributors = processWithoutProfiles(contributors, importer, activationContext);
activationContext = withProfiles(contributors, activationContext);
contributors = processWithProfiles(contributors, importer, activationContext);
// 将配置数据添加到环境对象中
applyToEnvironment(contributors, activationContext, importer.getLoadedLocations(),
importer.getOptionalLocations());
}
private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
ConfigDataImporter importer) {
this.logger.trace("Processing initial config data environment contributors without activation context");
// 调用ConfigDataEnvironmentContributors加载配置
contributors = contributors.withProcessedImports(importer, null);
registerBootstrapBinder(contributors, null, DENY_INACTIVE_BINDING);
return contributors;
}
private ConfigDataEnvironmentContributors processWithoutProfiles(ConfigDataEnvironmentContributors contributors,
ConfigDataImporter importer, ConfigDataActivationContext activationContext) {
this.logger.trace("Processing config data environment contributors with initial activation context");
contributors = contributors.withProcessedImports(importer, activationContext);
registerBootstrapBinder(contributors, activationContext, DENY_INACTIVE_BINDING);
return contributors;
}
private ConfigDataEnvironmentContributors processWithProfiles(ConfigDataEnvironmentContributors contributors,
ConfigDataImporter importer, ConfigDataActivationContext activationContext) {
this.logger.trace("Processing config data environment contributors with profile activation context");
contributors = contributors.withProcessedImports(importer, activationContext);
registerBootstrapBinder(contributors, activationContext, ALLOW_INACTIVE_BINDING);
return contributors;
}
processInitial、processWithoutProfiles及processWithProfiles的逻辑都类似,会调用ConfigDataEnvironmentContributors#withProcessedImports方法
java
ConfigDataEnvironmentContributors withProcessedImports(ConfigDataImporter importer,
ConfigDataActivationContext activationContext) {
ImportPhase importPhase = ImportPhase.get(activationContext);
this.logger.trace(LogMessage.format("Processing imports for phase %s. %s", importPhase,
(activationContext != null) ? activationContext : "no activation context"));
ConfigDataEnvironmentContributors result = this;
int processed = 0;
// 遍历所有的ConfigDataEnvironmentContributor
while (true) {
ConfigDataEnvironmentContributor contributor = getNextToProcess(result, activationContext, importPhase);
if (contributor == null) {
this.logger.trace(LogMessage.format("Processed imports for of %d contributors", processed));
return result;
}
if (contributor.getKind() == Kind.UNBOUND_IMPORT) {
ConfigDataEnvironmentContributor bound = contributor.withBoundProperties(result, activationContext);
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
result.getRoot().withReplacement(contributor, bound));
continue;
}
ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
result, contributor, activationContext);
ConfigDataLoaderContext loaderContext = new ContributorDataLoaderContext(this);
List<ConfigDataLocation> imports = contributor.getImports();
this.logger.trace(LogMessage.format("Processing imports %s", imports));
// 执行解析和加载数据
Map<ConfigDataResolutionResult, ConfigData> imported = importer.resolveAndLoad(activationContext,
locationResolverContext, loaderContext, imports);
this.logger.trace(LogMessage.of(() -> getImportedMessage(imported.keySet())));
ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
asContributors(imported));
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
result.getRoot().withReplacement(contributor, contributorAndChildren));
processed++;
}
}
调用ConfigDataImporter#resolveAndLoad
java
Map<ConfigDataResolutionResult, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
List<ConfigDataLocation> locations) {
try {
Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
// 解析配置文件路径
List<ConfigDataResolutionResult> resolved = resolve(locationResolverContext, profiles, locations);
// 加载配置文件
return load(loaderContext, resolved);
}
catch (IOException ex) {
throw new IllegalStateException("IO error on loading imports from " + locations, ex);
}
}
private Map<ConfigDataResolutionResult, ConfigData> load(ConfigDataLoaderContext loaderContext,
List<ConfigDataResolutionResult> candidates) throws IOException {
Map<ConfigDataResolutionResult, ConfigData> result = new LinkedHashMap<>();
// 从后往前读取
for (int i = candidates.size() - 1; i >= 0; i--) {
ConfigDataResolutionResult candidate = candidates.get(i);
ConfigDataLocation location = candidate.getLocation();
ConfigDataResource resource = candidate.getResource();
this.logger.trace(LogMessage.format("Considering resource %s from location %s", resource, location));
if (resource.isOptional()) {
this.optionalLocations.add(location);
}
if (this.loaded.contains(resource)) {
this.logger
.trace(LogMessage.format("Already loaded resource %s ignoring location %s", resource, location));
this.loadedLocations.add(location);
}
else {
try {
// 加载配置文件
ConfigData loaded = this.loaders.load(loaderContext, resource);
if (loaded != null) {
this.logger.trace(LogMessage.format("Loaded resource %s from location %s", resource, location));
this.loaded.add(resource);
this.loadedLocations.add(location);
result.put(candidate, loaded);
}
}
catch (ConfigDataNotFoundException ex) {
handle(ex, location, resource);
}
}
}
return Collections.unmodifiableMap(result);
}
读取文件是从最后一个开始读取,所以配置优先级顺序从高到低为:
file:./config/*/
file:./config/
file:./
classpath:/config/
classpath:/
这里通过StandardConfigDataLoader读取配置文件,StandardConfigDataLoader#load源码如下:
java
public ConfigData load(ConfigDataLoaderContext context, StandardConfigDataResource resource)
throws IOException, ConfigDataNotFoundException {
if (resource.isEmptyDirectory()) {
return ConfigData.EMPTY;
}
ConfigDataResourceNotFoundException.throwIfDoesNotExist(resource, resource.getResource());
StandardConfigDataReference reference = resource.getReference();
Resource originTrackedResource = OriginTrackedResource.of(resource.getResource(),
Origin.from(reference.getConfigDataLocation()));
String name = String.format("Config resource '%s' via location '%s'", resource,
reference.getConfigDataLocation());
// 调用PropertySourceLoader接口加载配置文件
List<PropertySource<?>> propertySources = reference.getPropertySourceLoader().load(name, originTrackedResource);
PropertySourceOptions options = (resource.getProfile() != null) ? PROFILE_SPECIFIC : NON_PROFILE_SPECIFIC;
return new ConfigData(propertySources, options);
}
PropertySourceLoader接口的实现类有PropertiesPropertySourceLoader和YamlPropertySourceLoader,其中PropertiesPropertySourceLoader可以加载properties和xml格式的配置文件,YamlPropertySourceLoader可以加载yml及yaml格式的配置文件