你是否有想过,Nacos
、Consul
是如何成为配置中心的,Spring
是如何读取到这些外部的配置文件的呢?
我之前就有这个疑问,直到上次看了一篇博客,解决MyBatisPlus
的数据源加密放在Nacos
的问题
在文档中,它获取了一个bean
,就是NacosPropertySourceLocator
,在这段代码中,获取到Nacos
上的数据源密文,然后进行解密,最后组装返回了PropertySource
对象。之后就能成功获取到解密后的数据源了,Mybatis
也就能正常使用了。
而在NacosPropertySourceLocator
其中,我发现了它实现了PropertySourceLocator
,在一番摸索之下,我明白了,这就是Spring
加载外部配置文件的关键。
那么本文,将会实现这个接口,完成加载外部自定义配置文件到服务之中,我们就简单点,用本地的一个文件来代替。
按照惯例,先看看这个接口,里面定义了什么方法
package org.springframework.cloud.bootstrap.config;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
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();
}
if (CompositePropertySource.class.isInstance(propertySource)) {
Collection<PropertySource<?>> sources = ((CompositePropertySource) propertySource).getPropertySources();
List<PropertySource<?>> filteredSources = new ArrayList<>();
for (PropertySource<?> p : sources) {
if (p != null) {
filteredSources.add(p);
}
}
return filteredSources;
}
else {
return Arrays.asList(propertySource);
}
}
}
一个方法locate()
,返回了PropertySource
,另一个是默认的方法可以返回PropertySource
的集合
源码非常简单,下面我们去实现一下这个接口
那么我们只需要实现这个方法就好了
package com.banmoon.business.config;
import cn.hutool.core.io.resource.ResourceUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.bootstrap.config.PropertySourceLocator;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.PropertySource;
import org.yaml.snakeyaml.Yaml;
import java.io.BufferedReader;
import java.util.Map;
@Slf4j
@Order(0)
@Configuration
public class CustomPropertySourceLocatorConfig implements PropertySourceLocator {
@Override
@SneakyThrows
public PropertySource<?> locate(Environment environment) {
BufferedReader reader = ResourceUtil.getUtf8Reader("D:\\test.yaml");
Yaml yaml = new Yaml();
Map<String, Object> yamlMap = yaml.loadAs(reader, Map.class);
return new MapPropertySource("customYaml", yamlMap);
}
}
非常简单,就是读取yaml
文件,然后生成一个PropertySource
返回出去
现在,我们准备一下test.yaml
,我们将它放到D
盘下,里面就简单放两个参数
user:
name: 半月无霜
age: 18
接下来,就是编码了,我们搞简单点,就直接在Main
启动类上添加读取上面文件的配置信息
package com.banmoon;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.Map;
@Slf4j
@EnableScheduling
@SpringBootApplication
public class WebBaseMain {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(WebBaseMain.class, args);
Environment environment = context.getBean(Environment.class);
Map<String, Object> map = environment.getProperty("user", Map.class);
log.info("user: {}", map);
}
}
但我执行后,没有效果?怎么回事,我寻找了半天,
原来就算添加了@Configuration
,将此类注册成为一个配置bean
,但由于这里面没有什么bean
需要注册的,所以也就是相当于一个空壳。所以这边还需要添加SPI
,Spring
将其自动注册成为一个配置类,就能去处理了。
在spring.factories
文件上添加
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.banmoon.business.config.CustomPropertySourceLocatorConfig
只要加上了,原本CustomPropertySourceLocatorConfig
类上的@Configuration
也不需要了
此时此刻,我们再启动服务,查看效果
好的,由于时间紧迫,上面的代码还是有点小瑕疵;后续想想如何改进一下
希望可以通过本篇文章,对PropertySourceLocator
这个接口有一个初步的认知,
再次说明,必须要在spring.factories
添加类,加注解是没有用的,这是spring
的SPI
机制
后面看情况会出篇这样的文章,看看spring
是如何通过这种方式加载bean
的
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。