springboot源码4---配置文件解析

执行时机

发布environment准备好事件时触发

public class SpringApplication {
    public ConfigurableApplicationContext run(String... args) {
        ...
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
        ...
	}          

	private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) {
	    ConfigurableEnvironment environment = this.getOrCreateEnvironment();
	    this.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());
	    ConfigurationPropertySources.attach((Environment)environment);
	    listeners.environmentPrepared((ConfigurableEnvironment)environment);
	    ...
	}
}

public class ConfigFileApplicationListener implements EnvironmentPostProcessor, SmartApplicationListener, Ordered {
    private static final String DEFAULT_PROPERTIES = "defaultProperties";
    private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/";
    private static final String DEFAULT_NAMES = "application";
    public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
    public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
    public static final String CONFIG_NAME_PROPERTY = "spring.config.name";
    public static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
    public static final String CONFIG_ADDITIONAL_LOCATION_PROPERTY = "spring.config.additional-location";

    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            this.onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent)event);
        }

        if (event instanceof ApplicationPreparedEvent) {
            this.onApplicationPreparedEvent(event);
        }

    }
}   

解析前工作

1.加载EnvironmentPostProcessor

  1. 通过SPI方式加载EnvironmentPostProcessor
  2. 添加ConfigFileApplicationListener自身
  3. 排序
  4. 遍历调用postProcessEnvironment
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
    //SPI加载EnvironmentPostProcessor
    List<EnvironmentPostProcessor> postProcessors = this.loadPostProcessors();
    //自身也添加进去
    postProcessors.add(this);
    AnnotationAwareOrderComparator.sort(postProcessors);
    Iterator var3 = postProcessors.iterator();

    while(var3.hasNext()) {
        EnvironmentPostProcessor postProcessor = (EnvironmentPostProcessor)var3.next();
        postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
    }

}

List<EnvironmentPostProcessor> loadPostProcessors() {
    return SpringFactoriesLoader.loadFactories(EnvironmentPostProcessor.class, this.getClass().getClassLoader());
}

public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
    this.addPropertySources(environment, application.getResourceLoader());
}

排序后的EnvironmentPostProcessor
在这里插入图片描述

2.添加随机数配置源

给environment添加随机数配置源,是为了解析类似${random.int}这种配置

protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
	RandomValuePropertySource.addToEnvironment(environment);
	(new ConfigFileApplicationListener.Loader(environment, resourceLoader)).load();
}

public static void addToEnvironment(ConfigurableEnvironment environment) {
    environment.getPropertySources().addAfter("systemEnvironment", new RandomValuePropertySource("random"));
    logger.trace("RandomValuePropertySource add to Environment");
}

添加后的配置源,优先级由高到低

在这里插入图片描述

3.SPI加载PropertySourceLoader

class Loader {
    Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        this.logger = ConfigFileApplicationListener.this.logger;
        this.loadDocumentsCache = new HashMap();
        this.environment = environment;
        this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
        this.resourceLoader = (ResourceLoader)(resourceLoader != null ? resourceLoader : new DefaultResourceLoader());
        //SPI加载PropertySourceLoader
        this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class, this.getClass().getClassLoader());
    }
}

加载了2个PropertySourceLoader

在这里插入图片描述

PropertiesPropertySourceLoader,支持properties、xml

public class PropertiesPropertySourceLoader implements PropertySourceLoader {
    private static final String XML_FILE_EXTENSION = ".xml";

    public PropertiesPropertySourceLoader() {
    }

    public String[] getFileExtensions() {
        return new String[]{"properties", "xml"};
    }

    public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
        Map<String, ?> properties = this.loadProperties(resource);
        return properties.isEmpty() ? Collections.emptyList() : Collections.singletonList(new OriginTrackedMapPropertySource(name, Collections.unmodifiableMap(properties), true));
    }

    private Map<String, ?> loadProperties(Resource resource) throws IOException {
        String filename = resource.getFilename();
        return (Map)(filename != null && filename.endsWith(".xml") ? PropertiesLoaderUtils.loadProperties(resource) : (new OriginTrackedPropertiesLoader(resource)).load());
    }
}

YamlPropertySourceLoader,支持yml、yaml

public class YamlPropertySourceLoader implements PropertySourceLoader {
    public YamlPropertySourceLoader() {
    }

    public String[] getFileExtensions() {
        return new String[]{"yml", "yaml"};
    }

    public List<PropertySource<?>> load(String name, Resource resource) throws IOException {
        if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", (ClassLoader)null)) {
            throw new IllegalStateException("Attempted to load " + name + " but snakeyaml was not found on the classpath");
        } else {
            List<Map<String, Object>> loaded = (new OriginTrackedYamlLoader(resource)).load();
            if (loaded.isEmpty()) {
                return Collections.emptyList();
            } else {
                List<PropertySource<?>> propertySources = new ArrayList(loaded.size());

                for(int i = 0; i < loaded.size(); ++i) {
                    String documentNumber = loaded.size() != 1 ? " (document #" + i + ")" : "";
                    propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber, Collections.unmodifiableMap((Map)loaded.get(i)), true));
                }

                return propertySources;
            }
        }
    }
}

开始解析

void load() {
    FilteredPropertySource.apply(this.environment, "defaultProperties", ConfigFileApplicationListener.LOAD_FILTERED_PROPERTY, (defaultProperties) -> {
        //当前激活的profile,双端队列
        this.profiles = new LinkedList();
        //已被处理过的profile
        this.processedProfiles = new LinkedList();
        //是否已被激活了,这个标志很重要,如果它为true,后续再激活的profile不会生效
        this.activatedProfiles = false;
        //缓存,暂存PropertySource,注意它是保持了profile解析顺序的LinkedHashMap,key=profile,value=MutablePropertySources
        this.loaded = new LinkedHashMap();
        //1、加载目前所有的profile,如果指定了spring.profiles.active,activatedProfiles=true
        this.initializeProfiles();
        //2、遍历profile进行解析,springboot支持多profile的原因
        while(!this.profiles.isEmpty()) {
  			ConfigFileApplicationListener.Profile profile = (ConfigFileApplicationListener.Profile)this.profiles.poll();
            if (this.isDefaultProfile(profile)) {
            	//把所有非null和default的profile加入environment
                this.addProfileToEnvironment(profile.getName());
            }

            this.load(profile, this::getPositiveProfileFilter, this.addToLoaded(MutablePropertySources::addLast, false));
            this.processedProfiles.add(profile);
        }
		//
        this.load((ConfigFileApplicationListener.Profile)null, this::getNegativeProfileFilter, this.addToLoaded(MutablePropertySources::addFirst, true));
        //3、把loaded缓存中的PropertySource加入environment
        this.addLoadedPropertySources();
        //4、更新environment的activeProfiles列表
        this.applyActiveProfiles(defaultProperties);
    });
}

1.加载所有profile

profile加载顺序如下

  1. api指定的profile,在环境准备阶段会加入environment的activeProfiles列表
  2. spring.profiles.include属性指定的profile,最后也会加入environment的activeProfiles列表
  3. spring.profiles.active属性指定的profile,在环境准备阶段会加入environment的activeProfiles列表

注意:与解析后加入environment中的PropertySource顺序相反

private void initializeProfiles() {
	//第1个是null,对应配置文件application.yaml
    this.profiles.add((Object)null);
 	//获取环境准备阶段spring.profiles.active指定的profile 
 	Set<ConfigFileApplicationListener.Profile> activatedViaProperty = this.getProfilesFromProperty("spring.profiles.active");
 	//通过环境准备阶段spring.profiles.include指定的子项profile
 	Set<ConfigFileApplicationListener.Profile> includedViaProperty = this.getProfilesFromProperty("spring.profiles.include");
    //获取环境准备阶段api方式指定的profile
    List<ConfigFileApplicationListener.Profile> otherActiveProfiles = this.getOtherActiveProfiles(activatedViaProperty, includedViaProperty);
    this.profiles.addAll(otherActiveProfiles);
    this.profiles.addAll(includedViaProperty);
    this.addActiveProfiles(activatedViaProperty);
    //没有指定任何profile时,default兜底
    if (this.profiles.size() == 1) {
        String[] var4 = this.environment.getDefaultProfiles();
        int var5 = var4.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String defaultProfileName = var4[var6];
            ConfigFileApplicationListener.Profile defaultProfile = new ConfigFileApplicationListener.Profile(defaultProfileName, true);
            this.profiles.add(defaultProfile);
        }
    }

}

private List<ConfigFileApplicationListener.Profile> getOtherActiveProfiles(Set<ConfigFileApplicationListener.Profile> activatedViaProperty, Set<ConfigFileApplicationListener.Profile> includedViaProperty) {
    return (List)Arrays.stream(this.environment.getActiveProfiles()).map(ConfigFileApplicationListener.Profile::new).filter((profile) -> {
        return !activatedViaProperty.contains(profile) && !includedViaProperty.contains(profile);
    }).collect(Collectors.toList());
}

void addActiveProfiles(Set<ConfigFileApplicationListener.Profile> profiles) {
     if (!profiles.isEmpty()) {
         if (this.activatedProfiles) {
             if (this.logger.isDebugEnabled()) {
                 this.logger.debug("Profiles already activated, '" + profiles + "' will not be applied");
             }

         } else {
             this.profiles.addAll(profiles);
             if (this.logger.isDebugEnabled()) {
                 this.logger.debug("Activated activeProfiles " + StringUtils.collectionToCommaDelimitedString(profiles));
             }
			 //设置activatedProfiles标志,后续激活的profile不再生效
             this.activatedProfiles = true;
             this.removeUnprocessedDefaultProfiles();
         }
     }
}

2.遍历profile解析

  1. 先解析不带profile的配置文件
  2. 其次解析带profile的配置文件
void load() {

	//第1个始终为null,即不带profile的配置文件,可能解析出新的激活profile放入队列
	while(!this.profiles.isEmpty()) {
	     ConfigFileApplicationListener.Profile profile = (ConfigFileApplicationListener.Profile)this.profiles.poll();
	     if (this.isDefaultProfile(profile)) {
	         this.addProfileToEnvironment(profile.getName());
	     }
	
	     this.load(profile, this::getPositiveProfileFilter, this.addToLoaded(MutablePropertySources::addLast, false));
	     this.processedProfiles.add(profile);
	}
	this.load((ConfigFileApplicationListener.Profile)null, this::getNegativeProfileFilter, this.addToLoaded(MutablePropertySources::addFirst, true));
	
	...
}

获取搜索路径和文件名

private void load(ConfigFileApplicationListener.Profile profile, ConfigFileApplicationListener.DocumentFilterFactory filterFactory, ConfigFileApplicationListener.DocumentConsumer consumer) {
       this.getSearchLocations().forEach((location) -> {
        //目录必须以/结尾,容易掉坑
        boolean isFolder = location.endsWith("/");
        Set<String> names = isFolder ? this.getSearchNames() : ConfigFileApplicationListener.NO_SEARCH_NAMES;
        names.forEach((name) -> {
            this.load(location, name, profile, filterFactory, consumer);
        });
    });
}

获取搜索路径,spring.config.location或者spring.config.additional-location + 4个默认路径(file:./config/, file:./, classpath:/config/, classpath:/,优先级从高到低)

private Set<String> getSearchLocations() {
    if (this.environment.containsProperty("spring.config.location")) {
        return this.getSearchLocations("spring.config.location");
    } else {
        Set<String> locations = this.getSearchLocations("spring.config.additional-location");
        locations.addAll(this.asResolvedSet(ConfigFileApplicationListener.this.searchLocations, "classpath:/,classpath:/config/,file:./,file:./config/"));
        return locations;
    }
}

获取搜索文件名,spring.config.name或者application

private Set<String> getSearchNames() {
    if (this.environment.containsProperty("spring.config.name")) {
        String property = this.environment.getProperty("spring.config.name");
        return this.asResolvedSet(property, (String)null);
    } else {
        return this.asResolvedSet(ConfigFileApplicationListener.this.names, "application");
    }
}

遍历搜索路径下的配置文件

依次遍历搜索路径下的properties、xml、yml、yaml等配置文件,然后拼接出完整路径:

  1. profile==null时,拼接规则:路径 + 文件名 + 后缀
  2. profile!=null时,拼接规则:路径 + 文件名-profile + 后缀
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {             
    //完整文件路径,直接根据后缀匹配出1个PropertySourceLoader
    if (!StringUtils.hasText(name)) {
        //PropertiesPropertySourceLoader和YamlPropertySourceLoader
        for (PropertySourceLoader loader : this.propertySourceLoaders) {
            if (canLoadFileExtension(loader, location)) {
            	//解析
                load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
                return;
            }
        }
    }
    
    Set<String> processed = new HashSet<>();
    //非完整文件,因为不知道后缀,所以尝试解析每个PropertySourceLoader支持的所有文件
    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) {
   if (profile != null) {
   	   //文件拼接规则1
       String profileSpecificFile = prefix + "-" + profile + fileExtension;
       this.load(loader, profileSpecificFile, profile, defaultFilter, consumer);
       this.load(loader, profileSpecificFile, profile, profileFilter, consumer);
       Iterator var10 = this.processedProfiles.iterator();

       while(var10.hasNext()) {
           ConfigFileApplicationListener.Profile processedProfile = (ConfigFileApplicationListener.Profile)var10.next();
           if (processedProfile != null) {
               String previouslyLoaded = prefix + "-" + processedProfile + fileExtension;
               this.load(loader, previouslyLoaded, profile, profileFilter, consumer);
           }
       }
   }
   //文件拼接规则2
   this.load(loader, prefix + fileExtension, profile, profileFilter, consumer);
}

解析、加入缓存

  1. 获取Rerousce,解析封装成PropertySource保存在Document中
  2. 如果当前还未激活profile,文件中的profile生效,加入profiles队列继续解析
  3. 删掉default
  4. 把解析后的PropertySource加入loaded缓存,LinkedHashMap保证了解析顺序
//缓存
private Map<ConfigFileApplicationListener.Profile, MutablePropertySources> loaded = new LinkedHashMap();

private void load(PropertySourceLoader loader, String location, Profile profile, DocumentFilter filter,
        DocumentConsumer consumer) {
    try {
        Resource resource = this.resourceLoader.getResource(location);
     	...
     	//获取Rerousce,解析封装成PropertySource保存在Document中
        //yaml中可能存在文档块,每个块就是1个Document
        List<Document> documents = loadDocuments(loader, name, resource);
        
        List<ConfigFileApplicationListener.Document> loaded = new ArrayList();
        for (ConfigFileApplicationListener.Document document: documents){
        	if (filter.match(document)) {
                this.addActiveProfiles(document.getActiveProfiles());
                this.addIncludedProfiles(document.getIncludeProfiles());
                loaded.add(document);
            }
        }
        
        Collections.reverse(loaded);
        
        if (!loaded.isEmpty()) {   
            //回调addToLoaded返回的lambda
            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);
    }
}

void addActiveProfiles(Set<ConfigFileApplicationListener.Profile> profiles) {
    //解析到了profile
    if (!profiles.isEmpty()) {
       if (this.activatedProfiles) {
           if (this.logger.isDebugEnabled()) {
               this.logger.debug("Profiles already activated, '" + profiles + "' will not be applied");
           }

    } else {
    	 //当前还未激活profile,文件中的profile才生效,
         this.profiles.addAll(profiles);
         if (this.logger.isDebugEnabled()) {
             this.logger.debug("Activated activeProfiles " + StringUtils.collectionToCommaDelimitedString(profiles));
         }

         this.activatedProfiles = true;
         //删除default profile,比如最开始未激活profile,此时profiles包括default,当加载application文件后发现激活了profile,此时就会走到这
         this.removeUnprocessedDefaultProfiles();
    }
}

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;
                }
            }
        }
        MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
                (k) -> new MutablePropertySources());
        //又回调MutablePropertySources::addFirst或utablePropertySources::addLast加入loaded缓存中
        addMethod.accept(merged, document.getPropertySource());
    };
}

3.缓存添加到environment

把缓存中的PropertySource反转后加入到environment最后,这也是为什么配置文件优先级低于命令行、系统属性、环境变量。

private void addLoadedPropertySources() {
    MutablePropertySources destination = this.environment.getPropertySources();
    
    List<MutablePropertySources> loaded = new ArrayList<>(this.loaded.values());
    //重要,保证了带profile的文件优先于不带profile的
    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();
            }
        }
    }
}

private void addLoadedPropertySource(Mutab lePropertySources destination, String lastAdded,
        PropertySource<?> source) {
    if (lastAdded == null) {
        if (destination.contains(DEFAULT_PROPERTIES)) {
            destination.addBefore(DEFAULT_PROPERTIES, source);
        }
        else {
            destination.addLast(source);
        }
    }
    else {
        destination.addAfter(lastAdded, source);
    }
}

所有配置文件加入environment后

在这里插入图片描述

4.更新environment的activeProfiles

private void applyActiveProfiles(PropertySource<?> defaultProperties) {
     List<String> activeProfiles = new ArrayList();
     if (defaultProperties != null) {
         Binder binder = new Binder(ConfigurationPropertySources.from(defaultProperties), new PropertySourcesPlaceholdersResolver(this.environment));
         activeProfiles.addAll(this.getDefaultProfiles(binder, "spring.profiles.include"));
         if (!this.activatedProfiles) {
             activeProfiles.addAll(this.getDefaultProfiles(binder, "spring.profiles.active"));
         }
     }
	 //去掉null和default
     this.processedProfiles.stream().filter(this::isDefaultProfile).map(ConfigFileApplicationListener.Profile::getName).forEach(activeProfiles::add);
     this.environment.setActiveProfiles((String[])activeProfiles.toArray(new String[0]));
 }

 private boolean isDefaultProfile(ConfigFileApplicationListener.Profile profile) {
     return profile != null && !profile.isDefaultProfile();
 }

总结

解析profile的优先级:api --> spring.profiles.include --> spring.profiles.active
加入environment的优先级:spring.profiles.active --> spring.profiles.include --> api
解析路径的优先级:file:./config/ --> file:./ --> classpath:/config/ --> classpath:/
命令行参数、系统属性、环境变量的优先级大于配置文件
profile激活原则:spring.profiles.active指定后,后续再指定不再生效
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值