赞
踩
写在前面:springboot是通过监听对应的 ApplicationEnvironmentPreparedEvent 事件来加载解析配置文件的。
从main方法开始,开始逐行分析
public static void main(String[] args)
{
SpringApplication.run(DemoApplication.class, args);
}
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);
}
对于springboot如何读取spring.factories文件,我们已经在前文中分析过了,所以此处我们直接看 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)) 方法,首先使用 getSpringFactoriesInstances(ApplicationListener.class) 方法在 spring.factories 文件中获取到key为 ApplicationListener 的所有类名称,然后根据反射创建对应的实例信息,在通过调用 setListeners 方法将创建好的所有监听器放到 SpringApplication 对象的 listeners 中,至此前期所需的listener对象就被创建完成了。
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();
}
将创建的 ApplicationListener 类型对应的实例信息添加到 listeners 中
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
上述的配置文件中会创建所有的ApplicationListener类型监听器,具体如下
Class | 作用 |
---|---|
ClearCachesApplicationListener | 在特定的应用程序事件发生时执行缓存清除操作 |
ParentContextCloserApplicationListener | 它用于处理父 Spring 上下文关闭的事件 |
CloudFoundryVcapEnvironmentPostProcessor | Spring Cloud 的一部分,专门用于在云平台上部署和运行应用程序时,将云平台提供的配置信息(通常是以 JSON 格式存储的环境变量)映射到 SpringBoot 配置属性中。 |
FileEncodingApplicationListener | 帮助应用程序正确地处理文件编码,以确保文件的读取和写入在不同操作系统上都能正常工作。 |
AnsiOutputApplicationListener | 是 SpringBoot 中的一个应用程序事件监听器,用于处理 ANSI 颜色输出的配置。 |
ConfigFileApplicationListener | 在应用程序启动过程中,负责加载和解析这些配置文件,并将配置属性注入到 SpringBoot 的环境中,以供应用程序使用。 |
DelegatingApplicationListener | 将事件监听器的注册和管理委托给其他监听器,允许将多个事件监听器组合在一起,以实现更复杂的事件处理逻辑。 |
ClasspathLoggingApplicationListener | 没什么实质性的作用,监听了ApplicationEnvironmentPreparedEvent 和 ApplicationFailedEvent 当这两个事件发生时,输出对应的信息 |
LoggingApplicationListener | 用于配置和管理应用程序的日志记录,是 SpringBoot 的一部分,允许在应用程序启动时配置和初始化日志系统。 |
LiquibaseServiceLocatorApplicationListener | SpringBoot 中的一个应用程序事件监听器,用于集成和配置 Liquibase 数据库迁移工具 |
BackgroundPreinitializer | 用于初始化一些与应用程序性能和功能相关的后台任务。 |
EventPublishingRunListener 是 SpringBoot 中的一个类,用于监听 SpringBoot 应用程序的生命周期事件,并在关键点发布相应的事件。
直接跟进到 run(String… args) 方法中,关于springboot解析配置文件只需要关注两个两个方法,即 getRunListeners(args) 和 prepareEnvironment(listeners, applicationArguments) 两个方法即可,我们将在接下来的篇幅中进行深度剖析,首先我们先来看 getRunListeners(args) 方法。
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); 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, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }
调用 getSpringFactoriesInstances(Class type, Class<?>[] parameterTypes, Object… args) 方法,
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
创建 SpringApplicationRunListener 类型对应的实例信息,即 EventPublishingRunListener 的对象。因为步骤都是一样的,所以我们直接来看 createSpringFactoriesInstances 方法,此处names集合中的元素就是我们需要创建的 EventPublishingRunListener 对象,此处就是反射最经典的用法,获取到 EventPublishingRunListener 的Class信息,通过Class信息获取到构造函数,然后调用构造函数,创建对应的 EventPublishingRunListener 实例信息,在创建 EventPublishingRunListener 实例信息的时候,还做了一些初始化操作,我们继续跟进。
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; }
继续跟进到 EventPublishingRunListener 的构造方法中,直接看第三行,创建 SimpleApplicationEventMulticaster 对象
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
// 创建了一个事件多播器,并将该事件多播器赋值给当前类的initialMulticaster变量
this.initialMulticaster = new SimpleApplicationEventMulticaster();
// application.getListeners()获取到的listeners就是我们前面所说的那些监听器集合,
// 调用多播器的addApplicationListener(ApplicationListener<?> listener)方法,开始注册事件监听器
for (ApplicationListener<?> listener : application.getListeners()) {
// 将当前的监听器对象注册到我们的事件多播器中
this.initialMulticaster.addApplicationListener(listener);
}
}
在创建事件多播器 SimpleApplicationEventMulticaster 对象时,调用 SimpleApplicationEventMulticaster 的父类的成员变量,在父类方法中创建了如下两个对象
private final DefaultListenerRetriever defaultRetriever = new DefaultListenerRetriever();
final Map<ListenerCacheKey, CachedListenerRetriever> retrieverCache = new ConcurrentHashMap<>(64);
在创建 DefaultListenerRetriever 对象时,还会创建如下的两个对象
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();
public final Set<String> applicationListenerBeans = new LinkedHashSet<>();
最终在上述步骤完成后,我们回到方法 getRunListeners,将 EventPublishingRunListener 的实例作为参数,创建 SpringApplicationRunListeners 对象,
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger,
getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
// 这里的listens实例集合中,实际上就只有一个对象,就是EventPublishingRunListener的实例
this.listeners = new ArrayList<>(listeners);
}
至此SpringApplicationRunListeners对象创建完成,接下来就要用SpringApplicationRunListeners对象去发布事件,从而正式解析配置文件
springboot就是在 prepareEnvironment(listeners, applicationArguments); 这个方法中进行解析的,我们具体来看看这个方法都干了一些什么事情,我们根据到源码中
public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); 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, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }
可以看到springboot的上下文环境就是根据 getOrCreateEnvironment() 创建的,根据我们的web应用类型类型选择创建不同的环境,一般情况下我们的都是servlet,所以创建一个标准的servlet环境;初次之外,如果是一个响应式编程类型的话,那么会创建一个标准的响应式web环境。 configureEnvironment(environment, applicationArguments.getSourceArgs()); 方法实际上是使用我们的main方法中传入的参数进行环境的配置,但是一般情况下main方法中args参数都默认是空的。ConfigurationPropertySources.attach(environment) 方法是配置配置文件的源信息。接下来 listeners.environmentPrepared(environment) 方法便是解析我们配置文件的主要方法了,我们将在接下来的篇幅中,着重剖析该方法。我们跟进到 ** listeners.environmentPrepared(environment)** 方法中
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
注意这里的listener,其实就是我们在前文提到过的 EventPublishingRunListener对象。该对象是 SpringApplicationRunListener 的具体实现,在 SpringApplicationRunListener 中定义了许多方法,其中包括应用开始启动(starting())、环境准备完成(environmentPrepared(ConfigurableEnvironment environment))、上下文准备完成(contextPrepared(ConfigurableApplicationContext context))、上下文加载完成(*contextLoaded(ConfigurableApplicationContext context) *)、启动完成(started(ConfigurableApplicationContext context))、应用正在运行(running(ConfigurableApplicationContext context))、运行失败(failed(ConfigurableApplicationContext context, Throwable exception))等方法,而这些方法也都对应了不同的事件,接下来我们着重分析是如何解析配置文件的。
解析配置文件事件上使用的就是 environmentPrepared(ConfigurableEnvironment environment) 方法。我们跟进到对应的方法中。
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
实际上就是使用了 SpringApplicationRunListener 的具体实现,也就是 EventPublishingRunListener 对象,然后调用 environmentPrepared 方法,在这里创建了一个 ApplicationEnvironmentPreparedEvent 对象即应用环境准备完成事件,然后通过 EventPublishingRunListener 的 initialMulticaster对象即多播器对象发布这个事件。好,接下来我们继续跟进。
@Override
public void environmentPrepared(ConfigurableEnvironment environment) {
this.initialMulticaster
.multicastEvent(new ApplicationEnvironmentPreparedEvent(this.application, this.args, environment));
}
resolveDefaultEventType(event) 方法实际上就是在解析我们 ApplicationEnvironmentPreparedEvent 类上的泛型信息,但是 ApplicationEnvironmentPreparedEvent 并没有指定泛型,所以该方法返回的值就是将我们的 ApplicationEnvironmentPreparedEvent 封装成了 ResolvableType 对象,我们继续跟进。
@Override
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
可以看到,第一行还是在解析我们当前事件的泛型信息,但是因为之前已经解析过了,所以会返回我们上一步的 ResolvableType 对象,然后获取一个 Executor 对象,但是因为我们都是同步的事件,所以该对象为空。紧接着我们看 getApplicationListeners(event, type) 方法,监听器的实例化也是就是在该方法中完成的,该方法会返回一个监听了我们当前事件的监听器集合,然后逐一调用对应监听器的 onApplicationEvent(event) 方法,去响应我们事件发生以后,需要进行的一些工作。调用 invokeListener(listener, event) 方法,根据名称我们就可以知道该此处就是在调用我们的listeren,我们继续往里跟进。
@Override
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);
}
}
}
可以看到,该方法的两个参数一个是我们的需要处理当前事件的监听器,另一个就是我们的事件对象;getErrorHandler() 方法返回的是一个 ErrorHandler 对象,作用就是当异常发生时,我们需要做的操作。但因为我们没有设置 ErrorHandler对象,所以会直接调用 *doInvokeListener(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);
}
}
在此调用了监听器中的,onApplicationEvent(event) 方法,这个方法是 ApplicationListener 接口的类,所以所有的子监听器对象都需要实现这个方法,因为springboot解析配置文件是通过 ConfigFileApplicationListener 类实现的,所以我们直接跟进到 ConfigFileApplicationListener 的 *onApplicationEvent(event)*方法中。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) { try { listener.onApplicationEvent(event); } catch (ClassCastException ex) { String msg = ex.getMessage(); if (msg == null || matchesClassCastMessage(msg, event.getClass())) { // Possibly a lambda-defined listener which we could not resolve the generic event type for // -> let's suppress the exception and just log a debug message. Log logger = LogFactory.getLog(getClass()); if (logger.isTraceEnabled()) { logger.trace("Non-matching event type for listener: " + listener, ex); } } else { throw ex; } } }
可以看到ConfigFileApplicationListener 监听了两个事件,分别是 ApplicationEnvironmentPreparedEvent(应用环境准备完成事件)和 ApplicationPreparedEvent(应用准备完成事件),此处我们的event对象是 ApplicationEnvironmentPreparedEvent 的实例,所以我们的看 onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event) 这个方法。
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
第一行可以看到此处是在加载我们的后置处理器,我们跟进去看一下加载的是什么类型后置处理器。可以看到,此处加载的是 EnvironmentPostProcessor 类型的后置处理器。所以我们的断言一下,EnvironmentPostProcessor 就是用来处理我们环境的后置处理器。回到正文,紧着调用 postProcessors.add(this) 方法会将我们当前的 ConfigFileApplicationListener 实例也会放到 postProcessors 集合中。紧接着遍历后置处理器集合,调用 postProcessEnvironment(event.getEnvironment(), event.getSpringApplication()) 方法。
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
}
}
List<EnvironmentPostProcessor> loadPostProcessors() {
return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, getClass().getClassLoader());
}
因为springboot解析配置文件是在ConfigFileApplicationListener中完成的,所以我们直接来看ConfigFileApplicationListener中的 postProcessEnvironment 方法
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
最终创建一个 Loader 对象,调用该对象的 loader() 方法完成配置文件的加载,最后将加载到的配置文件中的属性信息,存储在 environment中的 propertySources 属性中,完成配置文件的加载工作。
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
new Loader(environment, resourceLoader).load();
}
接下来我们首先看一下创建 Loader 对象的时候都做了那些事情
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { // 设置环境信息 this.environment = environment; // 创建了属性占位符解析器用来解析配置文件中的占位符 this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment); // 创建一个默认的资源加载器,其实就是我们的classLoader对象 this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(null); // 从spring.factories文件中加载PropertySourceLoader类型的实例,此处会加载两个实例分别是PropertiesPropertySourceLoader和YamlPropertySourceLoader, // 前者用来解析xml格式及properties格式的配置文件,后者用来解析yml格式及yaml格式的配置文件 this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, getClass().getClassLoader()); } // 在创建占位符解析器的时候会创建一个PropertyPlaceholderHelper对象,这个就是真正用来解析占位符的对象,占位符需要以"${"开始,以"}"结尾,键值对以":"分割 public PropertySourcesPlaceholdersResolver(Iterable<PropertySource<?>> sources, PropertyPlaceholderHelper helper) { this.sources = sources; this.helper = (helper != null) ? helper : new PropertyPlaceholderHelper(SystemPropertyUtils.PLACEHOLDER_PREFIX, SystemPropertyUtils.PLACEHOLDER_SUFFIX, SystemPropertyUtils.VALUE_SEPARATOR, true); }
接下来我们直接看 load 方法,
void load() { // DEFAULT_PROPERTIES = defaultProperties,LOAD_FILTERED_PROPERTY = "spring.profiles.active"和"spring.profiles.include" FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY, (defaultProperties) -> { this.profiles = new LinkedList<>(); this.processedProfiles = new LinkedList<>(); this.activatedProfiles = false; this.loaded = new LinkedHashMap<>(); // 调用initializeProfiles方法,封装profiles集合 initializeProfiles(); // 遍历this.profiles集合 while (!this.profiles.isEmpty()) { Profile profile = this.profiles.poll(); // 判断当前profile是不是默认的文件 if (isDefaultProfile(profile)) { addProfileToEnvironment(profile.getName()); } // 调用load方法,处理配置文件 load(profile, this::getPositiveProfileFilter, addToLoaded(MutablePropertySources::addLast, false)); this.processedProfiles.add(profile); } load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true)); // 将已经解析的propertySource添加到environment的propertySources中,完成配置文件的加载 addLoadedPropertySources(); applyActiveProfiles(defaultProperties); }); } static void apply(ConfigurableEnvironment environment, String propertySourceName, Set<String> filteredProperties, Consumer<PropertySource<?>> operation) { // 从environment中获取属性源信息 MutablePropertySources propertySources = environment.getPropertySources(); // 获取默认的属性源 PropertySource<?> original = propertySources.get(propertySourceName); // 当源默认的属性源为空时,调用上面的方法,开始处理配置文件 if (original == null) { operation.accept(null); return; } propertySources.replace(propertySourceName, new FilteredPropertySource(original, filteredProperties)); try { operation.accept(original); } finally { propertySources.replace(propertySourceName, original); } }
接下来我们先看 *initializeProfiles()*方法
private void initializeProfiles() {
// 先条件一个空的Profile对象入队,这个空的Profile对象,这个空的Profile对象就是用来处理默认的配置文件即"application.yml"文件的
this.profiles.add(null);
// 中间的代码略过
// 当profiles的集合中只有一个元素时,会在创建一个Profile对象,该对象的名称就是default
if (this.profiles.size() == 1) { // only has null profile
for (String defaultProfileName : this.environment.getDefaultProfiles()) {
Profile defaultProfile = new Profile(defaultProfileName, true);
// 将生成的对象添加到profiles集合中
this.profiles.add(defaultProfile);
}
}
}
紧着会调用到load方法的 load(profile, this::getPositiveProfileFilter,addToLoaded(MutablePropertySources::addLast, false)) 方法去处理配置文件信息
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
//获取到配置文件的地址,然后进行遍历
getSearchLocations().forEach((location) -> {
boolean isDirectory = location.endsWith("/");
Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
// 遍历names集合
names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
});
调用 load(location, name, profile, filterFactory, consumer) 方法
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { // 判断当前name是否为空 if (!StringUtils.hasText(name)) { for (PropertySourceLoader loader : this.propertySourceLoaders) { if (canLoadFileExtension(loader, location)) { load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer); return; } } throw new IllegalStateException("File extension of config file location '" + location + "' is not known to any PropertySourceLoader. If the location is meant to reference " + "a directory, it must end in '/'"); } Set<String> processed = new HashSet<>(); // 注意这里会从loader对象中获取propertySourceLoaders集合,其实就是我们前文中说的PropertiesPropertySourceLoader和YamlPropertySourceLoader两个对象的实例,一个是来解析"xml"和"properties"文件的,一个是用来解析"yml"和"yaml"的。此处我们直接看yml格式的配置文件解析过程 for (PropertySourceLoader loader : this.propertySourceLoaders) { for (String fileExtension : loader.getFileExtensions()) { if (processed.add(fileExtension)) { loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory, consumer); } } } }
private void loadForFileExtension(PropertySourceLoader loader, String prefix, String fileExtension, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) { DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null); DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile); // 这里会判断我们的profile对象是否为空,当第一次处理application.yml文件的时候,profile对象为空 if (profile != null) { String profileSpecificFile = prefix + "-" + profile + fileExtension; load(loader, profileSpecificFile, profile, defaultFilter, consumer); load(loader, profileSpecificFile, profile, profileFilter, consumer); // Try profile specific sections in files we've already processed for (Profile processedProfile : this.processedProfiles) { if (processedProfile != null) { String previouslyLoaded = prefix + "-" + processedProfile + fileExtension; load(loader, previouslyLoaded, profile, profileFilter, consumer); } } } // 调用load方法处理配置文件信息 load(loader, prefix + fileExtension, profile, profileFilter, consumer); }
当该方法执行完成,对应的配置文件也就解析完成了,源码中还有其他的很多逻辑,这里就不一一讲述了。
private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter, DocumentConsumer consumer) { Resource[] resources = getResources(location); for (Resource resource : resources) { try { // 此处略过多余的代码 String name = "applicationConfig: [" + getLocationName(location, resource) + "]"; // 此处调用loadDocuments方法,这里会将application.yml文件中定义的属性加载成Document对象 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); } continue; } List<Document> loaded = new ArrayList<>(); Collections.reverse(loaded); if (!loaded.isEmpty()) { // 将已经读取过的配置文件添加到loaded中,以便下次不会在重新读取 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) { StringBuilder description = getDescription("Failed to load property source from ", location, resource, profile); throw new IllegalStateException(description.toString(), ex); } } } } private List<Document> loadDocuments(PropertySourceLoader loader, String name, Resource resource) throws IOException { //创建一个DocumentsCacheKey对象,该对象是将配置文件加载读取完成后存放在loadDocumentsCache的key值,以便下次在执行时,如果当前DocumentsCacheKey对象对应的documents已经存在可以直接从内存中获取而不用在加载了 DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource); // 首先从loadDocumentsCache中获取当前cacheKey对应的documents是否存在,这里由于是第一次加载的,所以不存在 List<Document> documents = this.loadDocumentsCache.get(cacheKey); if (documents == null) { // 这里会调用YamlPropertySourceLoader的load方法去真正加载配置文件 List<PropertySource<?>> loaded = loader.load(name, resource); // 将配置读取到的配置文件封装成List<Document>对象 documents = asDocuments(loaded); // 添加到缓存中 this.loadDocumentsCache.put(cacheKey, documents); } return documents;
此处只展示在profile在为空对象时,处理appintcation.yml的情况;至于当该对象不为空时,流程大致都一样,就不在此一一解析了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。