SpringBoot基础系列之自定义配置源使用姿势实例演示

前面一篇博文介绍了一个@Value的一些知识点,其中提了一个点,@Value对应的配置,除了是配置文件中之外,可以从其他的数据源中获取么,如从 redis,db,http 中获取配置?
了解过 SpringCloud Config 的可以给出确切的答案,可以,而且用起来还老爽了,远程配置,支持配置动态刷新,接下来我们来看一下,在 SpringBoot 中,如何配置自定义的数据源
I. 项目环境
1. 项目依赖
本项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发
开一个 web 服务用于测试
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
II. 自定义配置源
@Value修饰的成员,绑定配置时,是从Envrionment中读取配置的,所以我们需要做的就是注册一个自定义的配置源,借助MapPropertySource可以来实现我们需求场景
1. 自定义数据源
演示一个最简单自定义的配置数据源,重写MapPropertySource的getProperties方法
实现如下
public class SimplePropertiesSource extends MapPropertySource {
public SimplePropertiesSource(String name, Map<String, Object> source) {
super(name, source);
}
public SimplePropertiesSource() {
this("filePropertiesSource", new HashMap<>());
}
/**
* 覆盖这个方法,适用于实时获取配置
*
* @param name
* @return
*/
@Override
public Object getProperty(String name) {
// 注意,只针对自定义开头的配置才执行这个逻辑
if (name.startsWith("selfdefine.")) {
return name + "_" + UUID.randomUUID();
}
return super.getProperty(name);
}
}
2. 数据源注册
上面只是声明了配置源,接下来把它注册到 Environment 中,这样就可以供应用使用了
@RestController
@SpringBootApplication
public class Application {
private Environment environment;
@Bean
public SimplePropertiesSource simplePropertiesSource(ConfigurableEnvironment environment) {
this.environment = environment;
SimplePropertiesSource ropertiesSource = new SimplePropertiesSource();
environment.getPropertySources().addLast(ropertiesSource);
return ropertiesSource;
}
// 获取配置
@GetMapping(path = "get")
public String getProperty(String key) {
return environment.getProperty(key);
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}

从上面的输出可以看出,自定义配置开头的会获取到随机的配置值;非selfdefine开头的,没有相应的配置,返回空
3. 基于文件的自定义配置源
上面这个可能有点过于儿戏了,接下来我们将配置源放在自定义的文件中,并支持文件配置修改
public class FilePropertiesSource extends MapPropertySource {
public FilePropertiesSource(String name, Map<String, Object> source) {
super(name, source);
}
public FilePropertiesSource() {
this("filePropertiesSource", new HashMap<>());
}
// 这种方式,适用于一次捞取所有的配置,然后从内存中查询对应的配置,提高服务性能
// 10s 更新一次
@PostConstruct
@Scheduled(fixedRate = 10_000)
public void refreshSource() throws IOException {
String ans =
FileCopyUtils.copyToString(new InputStreamReader(FilePropertiesSource.class.getClassLoader().getResourceAsStream("kv.properties")));
Map<String, Object> map = new HashMap<>();
for (String sub : ans.split("\n")) {
if (sub.isEmpty()) {
continue;
}
String[] kv = StringUtils.split(sub, "=");
if (kv.length != 2) {
continue;
}
map.put(kv[0].trim(), kv[1].trim());
}
source.clear();
source.putAll(map);
}
}
上面写了一个定时器,每 10s 刷新一下内存中的配置信息,当然这里也是可以配置一个文件变动监听器,相关有兴趣的话,可以看下Java 实现文件变动的监听可以怎么玩
对应的配置文件
user=xhh
name=一灰灰
age=18
注册的姿势与上面一致,就不单独说明了,接下来演示一下使用

从上可以看到文件中的配置修改之后,过一段时间会刷新
4. @Value绑定自定义配置
接下来我们看一下,将@Value绑定自定义的配置,是否可以成功
调整一下上面的 Application, 添加一个成员属性
@Value("${name}")
private String name;
@GetMapping(path = "get")
public String getProperty(String key) {
return name + "|" + environment.getProperty(key);
}
再次测试发现抛异常了,说是这个配置不存在!!!

(这就过分了啊,看了半天,结果告诉我不行,这还不得赶紧搞个差评么 )
已经写到这里了,当然我也得继续尝试挽救一下,为啥前面直接通过Environment可以拿到配置,但是@Value注解绑定就不行呢?
”罪魁祸首“就在于初始化顺序,我自定义的配置源,还没有塞到Envrionment,你就开会着手绑定了,就像准备给”一灰灰 blog“一个差评,结果发现还没关注...(好吧,我承认没关注也可以评论 )
根据既往的知识点(至于是哪些知识点,那就长话短说不了了,看下面几篇精选的博文吧)
- 【SpringBoot 基础系列-实战】如何指定 bean 最先加载(应用篇)
- SpringBoot 系列教程之 Bean 之指定初始化顺序的若干姿势
- SpringBoot 系列教程之 Bean 加载顺序之错误使用姿势辟谣
要解决这个问题,一个最简单的方式如下
创建一个独立的配置类,实现自定义数据源的注册
@Configuration
public class AutoConfig {
@Bean
public FilePropertiesSource filePropertiesSource(ConfigurableEnvironment environment) {
FilePropertiesSource filePropertiesSource = new FilePropertiesSource();
environment.getPropertySources().addLast(filePropertiesSource);
return filePropertiesSource;
}
}
测试类上指定 bean 依赖
@DependsOn("filePropertiesSource")
@EnableScheduling
@RestController
@SpringBootApplication
public class Application {
@Autowired
private Environment environment;
@Value("${name}")
private String name;
@GetMapping(path = "get")
public String getProperty(String key) {
return name + "|" + environment.getProperty(key);
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
再次测试,结果如下

从上面的演示动图可以看到,绑定自定义的数据源配置,没有问题,但是,当配置变更时,绑定的 name 字段,没有随之更新
简单来讲就是不支持动态刷新,这就难受了啊,我就想要动态刷新,那该怎么搞?
- 不要急,新的博文已经安排上了,下篇奉上(怕迷路的小伙伴,不妨关注一下”一灰灰 blog“)
5. 小结
最后按照惯例小结一下,本文篇幅虽长,但知识点比较集中,总结下来,两句话搞定
- 通过继承
MapPropertySource来实现自定义配置源,注册到Envrionment可供@Value使用 - 使用
@Value绑定自定义配置源时,注意注册的顺序要早于 bean 的初始化
好的,到这里正文结束, 我是一灰灰,欢迎各位大佬来踩一踩长草的公众号"一灰灰 blog"
III. 不能错过的源码和相关知识点
0. 项目
- 工程:https://github.com/liuyueyi/spring-boot-demo
- 源码: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/002-dynamic-envronment
配置系列博文
- 【SpringBoot 基础系列】@Value 中哪些你不知道的知识点
- 【SpringBoot 基础系列】ConfigurationProperties 配置绑定中那些你不知道的事情
- 【SpringBoot 基础系列】PropertySource 加载 Yaml 配置文件实例演示
- 【SpringBoot 基础系列】实现一个自定义配置加载器(应用篇)
- SpringBoot基础篇配置信息之配置刷新
- SpringBoot基础篇配置信息之自定义配置指定与配置内引用
- SpringBoot基础篇配置信息之多环境配置信息
- SpringBoot基础篇配置信息之如何读取配置信息
1. 一灰灰 Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现 bug 或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰 Blog 个人博客 https://blog.hhui.top
- 一灰灰 Blog-Spring 专题博客 http://spring.hhui.top

SpringBoot基础系列之自定义配置源使用姿势实例演示的更多相关文章
- 【SpringBoot 基础系列】实现一个自定义配置加载器(应用篇)
[SpringBoot 基础系列]实现一个自定义配置加载器(应用篇) Spring 中提供了@Value注解,用来绑定配置,可以实现从配置文件中,读取对应的配置并赋值给成员变量:某些时候,我们的配置可 ...
- SpringBoot基础系列-SpringBoot配置
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9990680.html SpringBoot基础系列-SpringBoot配置 概述 属性 ...
- SpringBoot基础系列-SpringCache使用
原创文章,转载请标注出处:<SpringBoot基础系列-SpringCache使用> 一.概述 SpringCache本身是一个缓存体系的抽象实现,并没有具体的缓存能力,要使用Sprin ...
- SpringBoot基础系列-使用日志
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9996897.html SpringBoot基础系列-使用日志 概述 SpringBoot ...
- 【SpringBoot基础系列】手把手实现国际化支持实例开发
[SpringBoot基础系列]手把手实现国际化支持实例开发 国际化的支持,对于app开发的小伙伴来说应该比价常见了:作为java后端的小伙伴,一般来讲接触国际化的机会不太多,毕竟业务开展到海外的企业 ...
- SpringBoot基础系列-使用Profiles
原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9996884.html SpringBoot基础系列-使用Profile 概述 Profi ...
- .Net Core 自定义配置源从配置中心读取配置
配置,几乎所有的应用程序都离不开它..Net Framework时代我们使用App.config.Web.config,到了.Net Core的时代我们使用appsettings.json,这些我们再 ...
- springboot~yml里的自定义配置~续
之前写了关于读取自定义配置的文章springboot~yml里的自定义配置,而今天主要说一下对复杂配置信息的读取方法,我们简单的配置用@Value注解就可以了,而结构复杂的一般使用@Configura ...
- springBoot基础系列--properties配置
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/7183408.html SpringBoot中免除了大部分手动配置,但是对于一些特定的情况, ...
随机推荐
- (翻译)Attacking Interoperability(攻击互操作性)in Black Hat 2009 研究报告
前言 攻击互操作性(Attacking Interoperability)是 Mark & Ryan & David 发表于 2009 年的美国黑帽大会(Black Hat)上的一份研 ...
- 分解uber依赖注入库dig-源码分析
上一篇帖子 分解uber依赖注入库dig-使用篇 把如何使用dig进行代码示例说明,这篇帖子分析dig的源码,看他是如何实现依赖注入的. dig实现的中心思想:所有传入Provide的函数必须要有除e ...
- 【JavaScript】Leetcode每日一题-最大整除子集
[JavaScript]Leetcode每日一题-最大整除子集 [题目描述] 给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对(an ...
- [BUUCTF-Pwn]hitcontraining_uaf
[BUUCTF-Pwn]hitcontraining_uaf 以此题作为对Pwn中堆利用的学习的开始.堆题初见,肯定有许多地方理解不恰当,希望师傅们能多多指教. 0x00.简述 成因 应用程序调用fr ...
- Scrum Meeting 2
Basic Info where:三号教学楼 when:2020/4/25 target: 简要汇报一下已完成任务,下一步计划与遇到的问题 Progress Team Member Position ...
- 【BUAA软工】提问回顾与个人总结
链接到以前提问题的博客 在之前的博客我曾经提问过以下几个问题 为什么单元测试必须由写程序的人完成? 过早优化,过早泛华:何时为过早? 为何使用goto语句? 用户需求分析:分而治之,如何分? 兼容性测 ...
- Go 函数详解
一.函数基础 函数由函数声明关键字 func.函数名.参数列表.返回列表.函数体组成 函数是一种类型.函数类型变量可以像其他类型变量一样使用,可以作为其他函数的参数或返回值,也可以直接调用执行 函数名 ...
- JS数组的操作方法汇总
数组的增删 push():添加到最后 pop():取出最后一个 shift():取出第一个 unshift():添加到第一个 splice() : 返回删除的数组,如果没有则为空数组,会改变原数组.可 ...
- 2021年主流CRM系统盘点
面对市面上五花八门的CRM系统,相信您在选择的时候肯定是一头雾水.哪个CRM系统功能最强大?哪个CRM系统性价比最高?哪个CRM系统最适合企业使用?本篇文章小编将选出几家有代表性的CRM系统,并进行对 ...
- JAVA基础——包机制
包机制 包的语法格式package pkg1[.pkg2[.pkg3...]] 一般利用 公司域名倒置 作为包名; 例如www.baidu.com,则建立报的名字com.baidu.www 一般不要让 ...