通常框架,启动以后都会尽快加载配置文件,springboot也不例外,下面就开始分析一下springboot加载配置文件的流程。spring
springboot配置的加载是从listener类开始的,还记得上一节我说listener类的调用没那么简单么,这一节就先从listener类的调用开始。springboot
run方法中,listeners初始化的地方。bash
public ConfigurableApplicationContext run(String... args) {
...
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
...
}
复制代码
listener类在SpringApplication对象初始化的时候,咱们已经从配置文件中获取到了,并存放在了集合里,那么这边为何没有直接调用而是又绕了一个逻辑呢,先进入getRunListeners方法。app
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
SpringApplicationRunListener.class, types, this, args));
}
复制代码
getRunListeners方法中,首先获取了SpringApplicationRunListener对象,而且使用SpringApplication,和args做为的构造函数的参数。而后在使用得到的SpringApplicationRunListener对象集合做为参数,构造了SpringApplicationRunListeners对象。 咱们先去看看SpringApplicationRunListener对象是啥。框架
getSpringFactoriesInstances这个方法你们 应该很熟了,从SpringApplication对象新建时候就一直在调用,因此咱们能够直接到配置文件中看一下,获取的SpringApplicationRunListener对象究竟是啥。dom
public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
public EventPublishingRunListener(SpringApplication application, String[] args) {
this.application = application;
this.args = args;
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
复制代码
在配置文件中发现了EventPublishingRunListener对象,这就是getRunListeners方法中得到到的SpringApplicationRunListener对象,构造函数很简单,我就不详细分析了。本来SpringApplication类中的listener对象,如今被封装到了EventPublishingRunListener对象中。ide
回过头来再看,SpringApplicationRunListener类又被封装到了SpringApplicationRunListeners对象中,这样getRunListeners方法的逻辑就执行完了。函数
如今看看listeners.starting()方法的调用逻辑。post
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
//遍历调用starting方法
listener.starting();
}
}
public void starting() {
//这个地方封装了一个事件,大概猜一下应该是打算使用策略模式
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
public void multicastEvent(ApplicationEvent event) {
multicastEvent(event, resolveDefaultEventType(event));
}
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//getApplicationListeners获取了符合策略的监听器
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
...
doInvokeListener(listener, event);
...
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
...
listener.onApplicationEvent(event);
...
}
复制代码
最后终于在doInvokeListener方法中,看到了监听器的执行。全部监听器的执行都使用的策略模式,若是想符合某些事件,在监听器的onApplicationEvent方法中配置一下便可。在这儿,咱们也能够感觉到spring框架设计的规范性,使用策略模式能够很方便的基于事件作相应扩展。ui
上面咱们已经了解了listener类的启动逻辑,下面开始正式分析配置文件的加载。
public ConfigurableApplicationContext run(String... args) {
...
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
...
}
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
...
listeners.environmentPrepared(environment);
...
}
复制代码
在run方法中,找到prepareEnvironment方法,进入以后,会看到监听器启动了environmentPrepared事件,因此咱们就去监听器里面,找找看符合环境事件的监听器。
看名字也能看出来,就是他ConfigFileApplicationListener。找到他的onApplicationEvent方法,开始分析。
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
//入口在这儿
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
postProcessors.add(this);
AnnotationAwareOrderComparator.sort(postProcessors);
for (EnvironmentPostProcessor postProcessor : postProcessors) {
//虽然这边还有其余几个监听器,可是最重要的依然是他自己因此,咱们仍是分析他自己的postProcessEnvironment方法
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
复制代码
postProcessEnvironment方法逻辑以下
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment,
ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
//关键代码在这儿
new Loader(environment, resourceLoader).load();
}
//先看构造函数
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
this.environment = environment;
this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(
this.environment);
this.resourceLoader = (resourceLoader != null) ? resourceLoader
: new DefaultResourceLoader();
//这个方法又见到了,话很少说,打开配置文件
this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
PropertySourceLoader.class, getClass().getClassLoader());
}
//这就是yml和properties配置支持的来源
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
复制代码
Loader类在构造函数中获取了yml和properties配置文件的支持。下面开始分析load函数。
public void load() {
...
//前面是关于profile的配置逻辑不复杂应该能够看懂,关键方法是load
load(null, this::getNegativeProfileFilter,
addToLoaded(MutablePropertySources::addFirst, true));
...
}
private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
//关注getSearchLocations方法
getSearchLocations().forEach((location) -> {
boolean isFolder = location.endsWith("/");
//关注getSearchNames方法
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach(
(name) -> load(location, name, profile, filterFactory, consumer));
});
}
复制代码
在getSearchLocations方法中,能够看到若是没有指定地址的话,默认地址就是"classpath:/,classpath:/config/,file:./,file:./config/",若是想指定的话,启动时须要加上spring.config.location参数
在getSearchNames方法中,能够看到若是没有指定配置文件名称的话,配置文件的名字就按照application来搜索。若是想指定的话,启动时须要加上spring.config.name参数
因此继续往下看load方法
private void load(String location, String name, Profile profile,
DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
...
Set<String> processed = new HashSet<>();
for (PropertySourceLoader loader : this.propertySourceLoaders) {
for (String fileExtension : loader.getFileExtensions()) {
if (processed.add(fileExtension)) {
loadForFileExtension(loader, location + name, "." + fileExtension,
profile, filterFactory, consumer);
}
}
}
}
复制代码
在这一层的load方法中,看到了Loader类新建时,获取的yml和properties格式支持类propertySourceLoaders,查看两个类的getFileExtensions方法
public class YamlPropertySourceLoader implements PropertySourceLoader {
@Override
public String[] getFileExtensions() {
return new String[] { "yml", "yaml" };
}
public class PropertiesPropertySourceLoader implements PropertySourceLoader {
private static final String XML_FILE_EXTENSION = ".xml";
@Override
public String[] getFileExtensions() {
return new String[] { "properties", "xml" };
}
复制代码
到了这一步,咱们终于摸清了为何默认的配置文件名字必须是application,并且能够为yml和properties格式。
最后加载的过程其实没啥好分析的了。通过了咱们的一通操做,咱们已经顺利的摸清了springboot默认配置加载的来源,而且了解了若是想指定配置该怎么作。