【基础系列】SpringBoot配置篇之PropertySource加载Yaml配置文件实例演示
source link: http://spring.hhui.top/spring-blog/2020/12/26/201226-SpringBoot系列PropertySource加载Yaml配置文件实例演示/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
在之前有介绍过借助注解 @PropertySource
来引入自定义的配置文件,在当时遇到抛出了一个问题,通过这个注解可以正确获取到 .properties
文件的配置信息,但是 yaml
文件却读取不到,最近又碰到这个问题,正好把之前挖的坑填上;本文将主要定位一下,为啥yml文件读取不了,又可以如何处理
如对之前博文有兴趣的小伙伴,可以查看: 180921-SpringBoot基础篇配置信息之自定义配置指定与配置内引用
I. 项目环境
1. 基本配置
本文后续的源码定位以及实例演示都是基于 SpringBoot 2.2.1.RELEASE
进行,如需复现本文中的case,请确保环境一致
- IDEA
- MAVEN
- SpringBoot 2.2.1.RELEASE
- JDK1.8
2. 实例项目
创建一个SpringBoot项目,用于后续的演示,首先创建一个配置文件 biz.properties
biz.token=mytoken biz.appKey=asdf biz.appVersion=1 biz.source=xxx.yyy biz.uuid=${biz.token}#${biz.appKey}
接下来定义对应的配置类
@Data @Configuration @PropertySource({"classpath:biz.properties"}) @ConfigurationProperties(prefix = "biz") public class OtherProperBean { private String token; private String appKey; private Integer appVersion; private String source; private String uuid; }
最后补上SpringBoot项目不可获取的启动类
/** * Created by @author yihui in 14:08 18/9/19. */ @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class); } }
II. PropertySource原理分析
想要定位为啥 @PropertySource
注解只会获取到 properties
文件的配置,而不能获取 yaml
文件配置信息,最直接的办法当然是直接撸源码(实际上最简单的办法直接借助搜索引擎,看一下有没有哪位大佬有过相关分享,如果不是为了写本文,我可是完全没想开撸,毕竟从提出这个问题到现在回复,也过了两年多了:sob:…)
1. 源码定位
那么这个源码可以怎么定位分析呢,先直接进入这个注解瞅一下
public @interface PropertySource { // ... 省略无关的属性 trueClass<? extends PropertySourceFactory> factory() default PropertySourceFactory.class; }
请注意上面的特意留出来的 PropertySourceFactory
, 从命名上来看,大致就能感觉这个工厂类与属性有关了,主要就是为了创建 PropertySource
对象
它就比较有意思了,如果没有猜错的话,配置文件加载到Spring容器之后,多半就会与 PropertySource
关联起来了(所以说好的命名可以省很多注释说明)
接下来看一下这个工厂类的默认实现 DefaultPropertySourceFactory
,源码很简单
public class DefaultPropertySourceFactory implements PropertySourceFactory { true@Override truepublic PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException { truetruereturn (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource)); true} }
在这里我们打个断点,确认一下会发生什么神器的事情
从上面的截图可以看到,这个 EncodedResource
包含了我们指定的配置文件,直接单步进去,可以看到执行的时候下面这个
// org.springframework.core.io.support.ResourcePropertySource#ResourcePropertySource(org.springframework.core.io.support.EncodedResource) public ResourcePropertySource(EncodedResource resource) throws IOException { truetruesuper(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource)); truetruethis.resourceName = null; }
请注意,核心代码不是 super()
这个构造方法,而是传参的 PropertiesLoaderUtils.loadProperties(resource)
上面这一行调用,就是实现具体的从配置文件中获取配置信息
下面是具体的实现(摘抄有用的部分逻辑)
// org.springframework.core.io.support.PropertiesLoaderUtils public static Properties loadProperties(EncodedResource resource) throws IOException { trueProperties props = new Properties(); truefillProperties(props, resource); truereturn props; } public static void fillProperties(Properties props, EncodedResource resource) throws IOException { // 属性填充,注意DefaultPropertiesPersister truefillProperties(props, resource, new DefaultPropertiesPersister()); } static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister) throws IOException { ... truetry { truetrueString filename = resource.getResource().getFilename(); truetrueif (filename != null && filename.endsWith(XML_FILE_EXTENSION)) { truetruetruestream = resource.getInputStream(); truetruetrue// 这个是关键 truetruetruepersister.loadFromXml(props, stream); truetrue} truetrueelse if (resource.requiresReader()) { truetruetruereader = resource.getReader(); truetruetrue// 关键调用 truetruetruepersister.load(props, reader); truetrue} truetrueelse { truetruetruestream = resource.getInputStream(); truetruetrue// 关键调用 truetruetruepersister.load(props, stream); truetrue} true} true... }
配置信息的读取,最终依靠的就是 org.springframework.util.DefaultPropertiesPersister#load()
,到这里我们基本上就找到了从配置文件中读取配置的“幕后黑手”,直接看一下它的实现逻辑就能知道为啥不支持yaml了
public class DefaultPropertiesPersister implements PropertiesPersister { true@Override truepublic void load(Properties props, InputStream is) throws IOException { truetrueprops.load(is); true} true@Override truepublic void load(Properties props, Reader reader) throws IOException { truetrueprops.load(reader); true} }
直接进入看到源码,非常简单直观的实现方式了,直接使用jdk的 java.util.Properties#load(java.io.InputStream)
来读取配置文件,所以真相已经大白了(原来都是jdk的锅:joy:)
2. yaml文件支持
经过上面的一番操作,我们知道 @ConfigurationProperties
加载配置文件,主要是借助jdk的 Properties#load
方法来读取配置文件到容器内,那么若我们希望加载yaml配置文件,可以怎么搞呢?
因为SpringBoot是支持yaml配置文件的读取的,所以我们完全可以扩展一下,借助SpringBoot的工具类来实现配置文件加载,所以可以实现自定义的 PropertySourceFactory
public class YamlSourceFactory extends DefaultPropertySourceFactory { @Override public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { if (resource == null) { return super.createPropertySource(name, resource); } // 这里使用Yaml配置加载类来读取yml文件信息 List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource()); return sources.get(0); } }
然后再我们希望使用的地方,利用自定义的工厂类替换默认的即可
@Data @Configuration @PropertySource(value = {"classpath:biz2.yml"}, factory = YamlSourceFactory.class) @ConfigurationProperties(prefix = "biz2.yml") public class YmlProperties { private Integer type; private String name; private List<Map<String, String>> ary; }
对应的配置文件如下
biz2: yml: type: 1 name: biz.yml.name ary: - a: hello - b: world
最后实例验证一下
@SpringBootApplication public class Application { public Application(YmlProperties ymlProperties) { System.out.println(ymlProperties); } public static void main(String[] args) { SpringApplication.run(Application.class); } }
3. 小结
当我们希望加载自定义的配置文件时, @PropertySource
注解是一个非常好的选择(当然也可以借助多环境配置方案,指定 spring.profiles.active
的值,实现加载前缀为 application-
的配置文件,有兴趣的小伙伴可以查看我之前的博文)
请注意 @PropertySource
引入的配置文件不支持 yaml
文件,如需支持,可以参考本文中的实现方式,自定义一个yaml文件的 PropertySourceFactory
最后提一句,遇到问题千万不要放过,尽量迅速解决,不要留待以后,不然拖延症发作的话,这个时间可能就一直悬着了…
III. 其他
0. 项目
项目源码
- 工程: https://github.com/liuyueyi/spring-boot-demo
- 源码: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/000-properties
系列博文
- 【基础系列】实现一个自定义配置加载器(应用篇)
- 【基础系列】SpringBoot配置信息之默认配置
- 【基础系列】SpringBoot配置信息之配置刷新
- 【基础系列】SpringBoot基础篇配置信息之自定义配置指定与配置内引用
- 【基础系列】SpringBoot基础篇配置信息之多环境配置信息
- 【基础系列】SpringBoot基础篇配置信息之如何读取配置信息
1. 一灰灰Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰Blog个人博客 https://blog.hhui.top
- 一灰灰Blog-Spring专题博客 http://spring.hhui.top
打赏 如果觉得我的文章对您有帮助,请随意打赏。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK