接口介绍
propertySourceLocator 是 Spring-cloud-context 包下的一个接口,用于定位外部数据源或者内部的自定义数据源配置。Nacos 就是通过 NacosPropertySourceLocator 类实现 propertySourceLocator 接口,来获取 nacos 的配置加载到环境中的。下面是接口定义的三个方法。这里我们需要关注 locate 这个方法。这个方法的实现依赖于我们如何去定义一个新的 propertySource
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public interface PropertySourceLocator { PropertySource<?> locate(Environment environment);
default Collection<PropertySource<?>> locateCollection(Environment environment) { return locateCollection(this, environment); }
static Collection<PropertySource<?>> locateCollection(PropertySourceLocator locator, Environment environment) { PropertySource<?> propertySource = locator.locate(environment); if (propertySource == null) { return Collections.emptyList(); } else if (propertySource instanceof CompositePropertySource) { Collection<PropertySource<?>> sources = ((CompositePropertySource)propertySource).getPropertySources(); List<PropertySource<?>> filteredSources = new ArrayList(); Iterator var5 = sources.iterator();
while(var5.hasNext()) { PropertySource<?> p = (PropertySource)var5.next(); if (p != null) { filteredSources.add(p); } }
return filteredSources; } else { return List.of(propertySource); } } }
|
项目示例
使用 propertySourceLocator 接口来加载我们的配置文件主要有三种方式,第一种是在程序启动后,利用 Spring 的 SPI 机制,读取 META-INF下的spring.factories
文件内配置的接口实现类全限定名,来完成实例化。第二种是使用 @PropertySource
注解来完成,不过使用注解的方式只支持读取 properties
类型的文件。
自定义属性
首先我们写一个自定义类,实现 PropertySourceLocator 这个接口,重写 locate 方法,方法返回值是我们自定义的 `PropertySource,之后 Spring 会自动将其加载到环境内。
1 2 3 4 5 6 7 8 9 10 11
| public class SoraPropertySourceLocator implements PropertySourceLocator { @Override public PropertySource<?> locate(Environment environment) { HashMap<String, Object> envMap = new HashMap<>() {{ put("sora.name", "sora33"); }}; return new MapPropertySource("sora", envMap); } }
|
之后我们要在 resources 下创建 META-INF (java 元数据) 文件夹,创建一个文件,名字必须为 spring.factories
,将我们的环境配置加载在主上下文之前,确保后面的主上下文在读取外部配置的时候可以读取到。写入如下(记得把注释删掉,不然会读取不到):
1 2 3
| org.springframework.cloud.bootstrap.BootstrapConfiguration=\ # 实现PropertySourceLocator接口的实现类路径 com.sora.config.SoraPropertySourceLocator
|
BootstrapConfiguration:引导过程阶段,这个阶段会在应用程序的主上下文之前执行,用于初始化一些在主上下文创建前完成的配置,例如服务配置、服务发现等。
写一个测试方法,读取我们刚刚配置好的自定义属性,注意,我们刚刚创建的属性名为 sora,Spring 会在前面固定加上一个 bootstrapProperties-
来表示上下文环境。
1 2 3 4 5 6 7 8 9 10 11
| @Resource private ConfigurableEnvironment configurableEnvironment;
@GetMapping("/test") public Result readProperties() { PropertySource<?> propertySource = configurableEnvironment.getPropertySources().get("bootstrapProperties-sora"); logger.info("自定义属性值:[{}]", propertySource.getProperty("sora.name")); return Result.success("SUCCESS"); }
|
通过 getProperty 方法可以看到成功读取到了我们刚才配置的属性。
下面我们继续来说一下读取外部文件,并且是 yml 格式的。在后面我们还有一个基于注解来读取外部文件,不过使用注解是读取不了 yml 格式的文件的。
首先我们创建一个 yml 格式的文件,直接放在 resources 目录下就可以,文件内容如下
1 2 3 4 5 6
| sora: age: 27 name: sora33 sex: 男 sakura: name: ayaneru
|
之后在方法内我们需要创建一个读取 yml 文件的对象,指定属性名和文件名,因为返回值是 List 类型,我们直接获取第一个元素拿到 propertySource
返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Override public PropertySource<?> locate(Environment environment) { final String fileName = "soraInfo.yml"; YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader(); try { List<PropertySource<?>> propertySources = sourceLoader.load("sora-data-yml", new ClassPathResource(fileName)); PropertySource<?> propertySource = propertySources.get(0); return propertySource; } catch (IOException e) { log.info("yml文件未找到!"); } return null; }
|
启动项目,成功读取到了 yml 文件的数据,也可以通过 getProperty 方法来获取属性值
读取外部文件
如果我们外部的配置文件格式是 properties
类型的,可以直接通过使用 @PropertySource
注解来完成配置的注入。这里我配置 classpath 为文件名,文件同样放在 resources 下就可以。属性名为 sora-data-properties。文件内容如下
1 2 3
| sora.name=sora33 sakura.name=ayaneru nayuta.type=cute
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Data @AllArgsConstructor @NoArgsConstructor @Configuration @PropertySource(value = "classpath:soraInfo.properties", name = "sora-data-properties") public class ReadOutFile {
@Value("${sora.name}") private String soraName; @Value("${sakura.name}") private String sakuraName; @Value("${nayuta.type}") private String nayutaType; }
|
成功读取到配置文件内的属性,需要注意的是,通过注解方式注入的属性名前面不会有 bootstrapProperties-
的前缀。
原理剖析
我们刚刚说了 spring 在启动的时候会创建一个 bootStrap
的 ApplicationContext(上下文),它是优先于主应用上下文之前加载的。所以我们可以分为几步来描述其实现原理:
- 创建 bootStrap 上下文:创建上下文环境,准备加载主应用上下文之前的配置
- 获取所有的
propertySourceLocator
:通过 Spring 的依赖注入机制,获取到所有的 propertySourceLocator 实现类
- 调用
locate方法
:通过调用 locate 方法,会获取一个 propertySource
对象
- 添加
propertySource
到环境内:将每一个 locate 方法返回的属性对象加入到 Environment
中,这样,当主应用上下文启动的时候,就可以从 Environment
获取到所有的配置了。