Spring Boot源码之旅五十三ConfigFileApplicationListener配置文件加载原理二

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

基本流程图

Loader的load加载配置文件

简单的说其实内部就是加载默认的配置文件,激活环境的文件,我们来看看细节。

    void load() {
    			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();
    						while (!this.profiles.isEmpty()) {
    							Profile profile = this.profiles.poll();
    							if (isDefaultProfile(profile)) {
    								addProfileToEnvironment(profile.getName());
    							}
    							load(profile, this::getPositiveProfileFilter,
    									addToLoaded(MutablePropertySources::addLast, false));
    							this.processedProfiles.add(profile);
    						}
    						load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
    						addLoadedPropertySources();
    						applyActiveProfiles(defaultProperties);
    					});
    		}

FilteredPropertySource的apply

首先默认取加载defaultProperties属性源,但是没有,所以就调用了传进来的lambda表达式。

initializeProfiles

定义了一些集合,用来做记录,然后我们看初始化做了什么。


这里有个想技巧,先放进一个null,为了后面可以优先处理这个,然后就可以去加载配置文件。后面还会去添加一个default默认的配置,也就是说此时profiles里有nulldefault两个配置。

    public static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
    
    public static final String INCLUDE_PROFILES_PROPERTY = "spring.profiles.include";
    
    
    
    		private void initializeProfiles() {
    			// The default profile for these purposes is represented as null. We add it
    			// first so that it is processed first and has lowest priority.
    			this.profiles.add(null);
    			Set<Profile> activatedViaProperty = getProfilesFromProperty(ACTIVE_PROFILES_PROPERTY);
    			Set<Profile> includedViaProperty = getProfilesFromProperty(INCLUDE_PROFILES_PROPERTY);
    			List<Profile> otherActiveProfiles = getOtherActiveProfiles(activatedViaProperty, includedViaProperty);
    			this.profiles.addAll(otherActiveProfiles);
    			// Any pre-existing active profiles set via property sources (e.g.
    			// System properties) take precedence over those added in config files.
    			this.profiles.addAll(includedViaProperty);
    			addActiveProfiles(activatedViaProperty);
    			if (this.profiles.size() == 1) { // only has null profile
    				for (String defaultProfileName : this.environment.getDefaultProfiles()) {
    					Profile defaultProfile = new Profile(defaultProfileName, true);
    					this.profiles.add(defaultProfile);
    				}
    			}
    		}

遍历profiles队列

这里就可以看到,遍历的时候第一个是null,所以先处理,不会去处理默认的default,他的思想就是如果第一次可以获取到配置,那就用这个配置,不用去获取默认的了。

isDefaultProfile

显然null是不满足的。

load加载文件

一个函数式编程的写法,其实就是获取所有的搜索路径getSearchLocations,挨个遍历执行lambda里面的方法,如果是个文件夹的话,搜索里面的文件名字集合,进行加载,否则就什么都不做,因为NO_SEARCH_NAMES是空集合

    		private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
    			getSearchLocations().forEach((location) -> {
    				boolean isFolder = location.endsWith("/");
    				Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
    				names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
    			});
    		}
getSearchLocations获取搜索路径

先看有没spring.config.location属性源,有的话从这里面的找路径,没有的话再看spring.config.additional-location属性源,最后把classpath:/,classpath:/config/,file:./,file:./config/这些路径也加上返回,这里就说明了我们的配置文件可以放在这里路径下,不过遍历的时候是反序的,也就是说优先级应该是file:./config/, file:./, classpath:/config/, classpath:/,项目目录优先,有config的优先。

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

默认就这4个:

getSearchNames获取搜做的名字(关键点)

这里很关键,首先是取找spring.config.name属性源,不存在的话再去找application属性源,所以为什么配置了bootstrap的话会先去找,因为在配置的时候加了spring.config.name=bootstrap属性源,加载完毕后再删除,然后就可以去加载application属性源了,其实环境准备时间出发了两次,所以这两个属性源都会被处理,这个后面说。

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

后面继续说拿到了配置文件名字后怎么加载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值