SpringBoot源码解析-配置文件的加载

喜欢ヅ旅行 2022-01-19 03:43 322阅读 0赞

一般框架,启动之后都会尽快加载配置文件,springboot也不例外,下面就开始分析一下springboot加载配置文件的流程。


springboot配置的加载是从listener类开始的,还记得上一节我说listener类的调用没那么简单么,这一节就先从listener类的调用开始。

run方法中,listeners初始化的地方。

  1. public ConfigurableApplicationContext run(String... args) {
  2. ...
  3. SpringApplicationRunListeners listeners = getRunListeners(args);
  4. listeners.starting();
  5. ...
  6. }
  7. 复制代码

listener类在SpringApplication对象初始化的时候,我们已经从配置文件中获取到了,并存放在了集合里,那么这边为什么没有直接调用而是又绕了一个逻辑呢,先进入getRunListeners方法。

  1. private SpringApplicationRunListeners getRunListeners(String[] args) {
  2. Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
  3. return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
  4. SpringApplicationRunListener.class, types, this, args));
  5. }
  6. 复制代码

getRunListeners方法中,首先获取了SpringApplicationRunListener对象,并且使用SpringApplication,和args作为的构造函数的参数。然后在使用获得的SpringApplicationRunListener对象集合作为参数,构造了SpringApplicationRunListeners对象。 我们先去看看SpringApplicationRunListener对象是啥。

getSpringFactoriesInstances这个方法大家 应该很熟了,从SpringApplication对象新建时候就一直在调用,所以我们可以直接到配置文件中看一下,获取的SpringApplicationRunListener对象到底是啥。

  1. public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {
  2. public EventPublishingRunListener(SpringApplication application, String[] args) {
  3. this.application = application;
  4. this.args = args;
  5. this.initialMulticaster = new SimpleApplicationEventMulticaster();
  6. for (ApplicationListener<?> listener : application.getListeners()) {
  7. this.initialMulticaster.addApplicationListener(listener);
  8. }
  9. }
  10. 复制代码

在配置文件中发现了EventPublishingRunListener对象,这就是getRunListeners方法中获得到的SpringApplicationRunListener对象,构造函数很简单,我就不详细分析了。原本SpringApplication类中的listener对象,现在被封装到了EventPublishingRunListener对象中。

回过头来再看,SpringApplicationRunListener类又被封装到了SpringApplicationRunListeners对象中,这样getRunListeners方法的逻辑就执行完了。

现在看看listeners.starting()方法的调用逻辑。

  1. public void starting() {
  2. for (SpringApplicationRunListener listener : this.listeners) {
  3. //遍历调用starting方法
  4. listener.starting();
  5. }
  6. }
  7. public void starting() {
  8. //这个地方封装了一个事件,大概猜一下应该是打算使用策略模式
  9. this.initialMulticaster.multicastEvent(
  10. new ApplicationStartingEvent(this.application, this.args));
  11. }
  12. public void multicastEvent(ApplicationEvent event) {
  13. multicastEvent(event, resolveDefaultEventType(event));
  14. }
  15. public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
  16. ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
  17. //getApplicationListeners获取了符合策略的监听器
  18. for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
  19. Executor executor = getTaskExecutor();
  20. if (executor != null) {
  21. executor.execute(() -> invokeListener(listener, event));
  22. }
  23. else {
  24. invokeListener(listener, event);
  25. }
  26. }
  27. }
  28. protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
  29. ...
  30. doInvokeListener(listener, event);
  31. ...
  32. }
  33. private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
  34. ...
  35. listener.onApplicationEvent(event);
  36. ...
  37. }
  38. 复制代码

最后终于在doInvokeListener方法中,看到了监听器的执行。所有监听器的执行都使用的策略模式,如果想符合某些事件,在监听器的onApplicationEvent方法中配置一下即可。在这儿,我们也可以感受到spring框架设计的规范性,使用策略模式可以很方便的基于事件做相应扩展。


上面我们已经了解了listener类的启动逻辑,下面开始正式分析配置文件的加载。

  1. public ConfigurableApplicationContext run(String... args) {
  2. ...
  3. ApplicationArguments applicationArguments = new DefaultApplicationArguments(
  4. args);
  5. ConfigurableEnvironment environment = prepareEnvironment(listeners,
  6. applicationArguments);
  7. ...
  8. }
  9. private ConfigurableEnvironment prepareEnvironment(
  10. SpringApplicationRunListeners listeners,
  11. ApplicationArguments applicationArguments) {
  12. ...
  13. listeners.environmentPrepared(environment);
  14. ...
  15. }
  16. 复制代码

在run方法中,找到prepareEnvironment方法,进入之后,会看到监听器启动了environmentPrepared事件,所以我们就去监听器里面,找找看符合环境事件的监听器。

看名字也能看出来,就是他ConfigFileApplicationListener。找到他的onApplicationEvent方法,开始分析。

  1. public void onApplicationEvent(ApplicationEvent event) {
  2. if (event instanceof ApplicationEnvironmentPreparedEvent) {
  3. //入口在这儿
  4. onApplicationEnvironmentPreparedEvent(
  5. (ApplicationEnvironmentPreparedEvent) event);
  6. }
  7. if (event instanceof ApplicationPreparedEvent) {
  8. onApplicationPreparedEvent(event);
  9. }
  10. }
  11. private void onApplicationEnvironmentPreparedEvent(
  12. ApplicationEnvironmentPreparedEvent event) {
  13. List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
  14. postProcessors.add(this);
  15. AnnotationAwareOrderComparator.sort(postProcessors);
  16. for (EnvironmentPostProcessor postProcessor : postProcessors) {
  17. //虽然这边还有其他几个监听器,但是最重要的依然是他本身所以,我们还是分析他本身的postProcessEnvironment方法
  18. postProcessor.postProcessEnvironment(event.getEnvironment(),
  19. event.getSpringApplication());
  20. }
  21. }
  22. 复制代码

postProcessEnvironment方法逻辑如下

  1. public void postProcessEnvironment(ConfigurableEnvironment environment,
  2. SpringApplication application) {
  3. addPropertySources(environment, application.getResourceLoader());
  4. }
  5. protected void addPropertySources(ConfigurableEnvironment environment,
  6. ResourceLoader resourceLoader) {
  7. RandomValuePropertySource.addToEnvironment(environment);
  8. //关键代码在这儿
  9. new Loader(environment, resourceLoader).load();
  10. }
  11. //先看构造函数
  12. Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
  13. this.environment = environment;
  14. this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(
  15. this.environment);
  16. this.resourceLoader = (resourceLoader != null) ? resourceLoader
  17. : new DefaultResourceLoader();
  18. //这个方法又见到了,话不多说,打开配置文件
  19. this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(
  20. PropertySourceLoader.class, getClass().getClassLoader());
  21. }
  22. //这就是yml和properties配置支持的来源
  23. org.springframework.boot.env.PropertySourceLoader=\
  24. org.springframework.boot.env.PropertiesPropertySourceLoader,\
  25. org.springframework.boot.env.YamlPropertySourceLoader
  26. 复制代码

Loader类在构造函数中获取了yml和properties配置文件的支持。下面开始分析load函数。

  1. public void load() {
  2. ...
  3. //前面是关于profile的配置逻辑不复杂应该可以看懂,关键方法是load
  4. load(null, this::getNegativeProfileFilter,
  5. addToLoaded(MutablePropertySources::addFirst, true));
  6. ...
  7. }
  8. private void load(Profile profile, DocumentFilterFactory filterFactory,
  9. DocumentConsumer consumer) {
  10. //关注getSearchLocations方法
  11. getSearchLocations().forEach((location) -> {
  12. boolean isFolder = location.endsWith("/");
  13. //关注getSearchNames方法
  14. Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
  15. names.forEach(
  16. (name) -> load(location, name, profile, filterFactory, consumer));
  17. });
  18. }
  19. 复制代码

在getSearchLocations方法中,可以看到如果没有指定地址的话,默认地址就是”classpath:/,classpath:/config/,file:./,file:./config/“,如果想指定的话,启动时需要加上spring.config.location参数

在getSearchNames方法中,可以看到如果没有指定配置文件名称的话,配置文件的名字就按照application来搜索。如果想指定的话,启动时需要加上spring.config.name参数

所以继续往下看load方法

  1. private void load(String location, String name, Profile profile,
  2. DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
  3. ...
  4. Set<String> processed = new HashSet<>();
  5. for (PropertySourceLoader loader : this.propertySourceLoaders) {
  6. for (String fileExtension : loader.getFileExtensions()) {
  7. if (processed.add(fileExtension)) {
  8. loadForFileExtension(loader, location + name, "." + fileExtension,
  9. profile, filterFactory, consumer);
  10. }
  11. }
  12. }
  13. }
  14. 复制代码

在这一层的load方法中,看到了Loader类新建时,获取的yml和properties格式支持类propertySourceLoaders,查看两个类的getFileExtensions方法

  1. public class YamlPropertySourceLoader implements PropertySourceLoader {
  2. @Override
  3. public String[] getFileExtensions() {
  4. return new String[] { "yml", "yaml" };
  5. }
  6. public class PropertiesPropertySourceLoader implements PropertySourceLoader {
  7. private static final String XML_FILE_EXTENSION = ".xml";
  8. @Override
  9. public String[] getFileExtensions() {
  10. return new String[] { "properties", "xml" };
  11. }
  12. 复制代码

到了这一步,我们终于摸清了为什么默认的配置文件名字必须是application,而且可以为yml和properties格式。

最后加载的过程其实没啥好分析的了。经过了我们的一通操作,我们已经顺利的摸清了springboot默认配置加载的来源,并且了解了如果想指定配置该怎么做。


返回目录

转载于:https://juejin.im/post/5c8f9b0ff265da612c3a34ac

发表评论

表情:
评论列表 (有 0 条评论,322人围观)

还没有评论,来说两句吧...

相关阅读