一、介绍
相信绝大多数使用springboot开发项目的朋友们在添加配置时,通常都是通过以下几种方式:
- 在classpath下添加application.yml或application.properties配置文件,或通过
spring.config.location
指定配置文件位置。 - 通过
spring.config.additional-location
指定额外的配置文件位置。 - 通过
spring.config.import
导入指定位置的配置文件。
但无论通过哪种方式,其配置的形式都是通过在配置文件中通过key - value
的形式添加具体配置的,且配置文件类型为yaml
或properties
。如下所示:
properties文件内容示例
key1 = value1
yaml文件内容示例
key1: value1
其中key1
作为配置名,value1
作为配置值。
今天给大家介绍另一种配置形式,该配置使用文件名
作为配置名,文件内容
作为配置值。
如文件名为username
的内容如下:
admin
文件名为password
的内容如下:
123456
二、演示环境
本演示项目的环境如下:
- java:1.8
- springboot:2.4.3
三、项目演示
本项目演示的是,在指定目录中添加配置文件,并以文件名为key,文件内容为value;然后在application.yml
配置文件中通过spring.config.import
指定configtree
将目录中的所有配置文件添加到项目的环境中,并通过placeholder${}
的形式获取配置。
1. 配置文件
我们在本地文件系统中添加配置文件,其目录结构如下所示
/
etc/
app/
config/
admin/
username
password
db/
username
password
nacos/
username
password
各个配置文件内容如下所示
/etc/app/config/admin/username
admin
/etc/app/config/admin/password
123456
/etc/app/config/db/username
mysql
/etc/app/config/db/password
123456
/etc/app/config/nacos/username
nacos
/etc/app/config/nacos/password
nacos
2. 导入配置
在application.yml
配置文件中添加配置spring.config.import
。
注意,当我们要添加以文件名为key,文件内容为value的配置文件时,必须在路径前添加前缀configtree:
,且路径最后以/
结尾。另外,该路径支持*
通配符。
spring:
config:
import:
- configtree:/etc/app/config/
如上所示,springboot将读取路径/etc/app/config/
(包括子目录)中的所有文件,并以文件名为key,文件内容为value。
3. 检测配置属性
当我们按照上面示例配置时,由于在application.yml
中配置的spring.config.import
目录为/etc/app/config/
,因此我们可以通过admin.username
、admin.password
、db.username
、db.password
、nacos.username
和nacos.password
获取对应文件内容的值。
下面我们启动项目对其进行检验。
启动项目:
输出:
四、应用场景
看到这里,想必很多小伙伴虽然知道springboot如何通过spring.config.import
+ configtree
来读取以文件名为key,文件内容为value的配置,但是这种配置方式使用起来并不方便,且一个文件仅对应一个配置属性,那如果需要大量配置岂不是要创建大量文件?
其实,使用该配置方式和使用application.yml
方式应该是相辅相成的,两者应当配合使用。
当我们在云平台(比如docker)上运行应用程序时,有时需要读取容器提供的配置值。而我们多数情况下都是通过该容器的环境变量来获取所需的配置,但是如果我们可能会频繁修改该环境变量或该变量需要加密时,就可能暴露出它的缺点了。因为容器的环境变量是在创建镜像的时候就确定的,当我们需要修改该环境变量时就意味着已经创建的容器需要删除了。
所以我们可以通过容器挂载卷的方式,将该环境变量保存在文件中,通过挂载卷将配置文件挂载到容器中。
五、源码解析
在前面我们源码分析[springboot如何创建并配置环境3 - 配置扩展属性2]文章中,简单介绍过springboot通过ConfigTreeConfigDataLocationResolver
和StandardConfigDataLocationResolver
两种配置文件位置解析器来解析配置文件的位置,然后通过ConfigTreeConfigDataLoader
和StandardConfigDataLoader
来加载对应配置文件中的配置内容。
因此结合本文重点,我们应主要关注ConfigTreeConfigDataLocationResolver
和ConfigTreeConfigDataLoader
是如何解析配置文件的位置并从文件中读取配置内容的。
1. ConfigTreeConfigDataLocationResolver
首先我们查看ConfigTreeConfigDataLocationResolver
是如何解析出配置文件目录的,主要分两步:①判断配置的路径是否满足解析的条件,②解析配置文件的位置。
判断配置的路径是否满足解析的条件
该判断逻辑通过
isResolvable()
方法完成,主要判断依据就是配置的spring.config.import
值是否包含configtree:
前缀,如果包含,则满足条件。private static final String PREFIX = "configtree:"; @Override public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) { return location.hasPrefix(PREFIX); }
解析配置文件的位置
该逻辑通过方法
resolve()
完成,其目的是根据配置的spring.config.import
目录转换为该目录下文件的资源。@Override public List<ConfigTreeConfigDataResource> resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location) { try { return resolve(context, location.getNonPrefixedValue(PREFIX)); } catch (IOException ex) { throw new ConfigDataLocationNotFoundException(location, ex); } } private List<ConfigTreeConfigDataResource> resolve(ConfigDataLocationResolverContext context, String location) throws IOException { // 目录必须以“/结尾” Assert.isTrue(location.endsWith("/"), () -> String.format("Config tree location '%s' must end with '/'", location)); // 如果目录不是通配符的形式,则直接根据该目录获取该目录下文件的资源集合。 if (!this.resourceLoader.isPattern(location)) { return Collections.singletonList(new ConfigTreeConfigDataResource(location)); } // 如果目录是通配符的形式,则对其进一步处理,获取该目录下文件的资源集合。 Resource[] resources = this.resourceLoader.getResources(location, ResourceType.DIRECTORY); List<ConfigTreeConfigDataResource> resolved = new ArrayList<>(resources.length); for (Resource resource : resources) { resolved.add(new ConfigTreeConfigDataResource(resource.getFile().toPath())); } return resolved; }
2. ConfigTreeConfigDataLoader
然后我们分析ConfigTreeConfigDataLoader
是如何根据配置文件资源加载其内容的。
在加载配置属性中,我们看到该方法主要分两步,①根据文件资源获取文件路径path,②根据文件路径获取该文件中的配置。我们在方法结束时添加断点,然后启动项目,让代码运行到断点处。如下图所示,我们发现springboot已经按照预期将各个配置文件读取成功了。
任意点击其中一个元素,可以看到配置文件中的内容也已经被加载了
最后将其封装到ConfigData
对象中返回。
六、总结
- 通过
spring.config.import
+configtree:
前缀的方式,加载以文件名为key、文件内容为value的配置属性。 configtree:
应以/
结尾。- 适用于代替在云平台中读取加密的系统环境变量的场景。
纸上得来终觉浅,绝知此事要躬行。
————————————————我是万万岁,我们下期再见————————————————