一灰灰blog 一灰灰blog
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)

一灰灰blog

资深搬运工
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 高可用
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)
  • 基础系列

    • 配置

      • 【基础系列】SpringBoot基础篇配置信息之如何读取配置信息
      • 【基础系列】SpringBoot基础篇配置信息之多环境配置信息
      • 【基础系列】SpringBoot基础篇配置信息之自定义配置指定与配置内引用
      • 【基础系列】SpringBoot配置信息之配置刷新
      • 【基础系列】SpringBoot配置信息之默认配置
      • 【基础系列】实现一个自定义配置加载器(应用篇)
      • 【基础系列】SpringBoot配置篇之PropertySource加载Yaml配置文件实例演示
        • I. 项目环境
          • 1. 基本配置
          • 2. 实例项目
        • II. PropertySource原理分析
          • 1. 源码定位
          • 2. yaml文件支持
          • 3. 小结
        • III. 其他
          • 0. 项目
          • 1. 一灰灰Blog
      • 【基础系列】ConfigurationProperties配置绑定中那些你不知道的事情
      • 【基础系列】SpringBoot基础篇@Value中哪些你不知道的知识点
      • 【基础系列】SpringBoot之自定义配置源的使用姿势
      • 【基础系列】SpringBoot @Value之字面量及SpEL知识点介绍篇
      • 【基础系列】SpringBoot应用篇@Value注解支持配置自动刷新能力扩展
      • 【基础系列】基于maven多环境配置
    • AOP

    • Bean

    • SpEL

    • 事件

    • 国际化

    • 定时器

    • 日志

  • DB系列

  • 搜索系列

  • MQ系列

  • WEB系列

  • 中间件

  • 运维

  • SpringSecurity

  • SpringCloud

  • Spring系列
  • 基础系列
  • 配置
一灰灰
2020-12-26

【基础系列】SpringBoot配置篇之PropertySource加载Yaml配置文件实例演示

在之前有介绍过借助注解@PropertySource来引入自定义的配置文件,在当时遇到抛出了一个问题,通过这个注解可以正确获取到.properties文件的配置信息,但是yaml文件却读取不到,最近又碰到这个问题,正好把之前挖的坑填上;本文将主要定位一下,为啥yml文件读取不了,又可以如何处理

如对之前博文有兴趣的小伙伴,可以查看: 180921-SpringBoot基础篇配置信息之自定义配置指定与配置内引用 (opens new window)

# 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}
1
2
3
4
5
6

接下来定义对应的配置类

@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;
}
1
2
3
4
5
6
7
8
9
10
11

最后补上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);
    }
}
1
2
3
4
5
6
7
8
9

# II. PropertySource原理分析

想要定位为啥@PropertySource注解只会获取到properties文件的配置,而不能获取yaml文件配置信息,最直接的办法当然是直接撸源码(实际上最简单的办法直接借助搜索引擎,看一下有没有哪位大佬有过相关分享,如果不是为了写本文,我可是完全没想开撸,毕竟从提出这个问题到现在回复,也过了两年多了😭...)

# 1. 源码定位

那么这个源码可以怎么定位分析呢,先直接进入这个注解瞅一下

public @interface PropertySource {
  // ... 省略无关的属性

	Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
1
2
3
4
5

请注意上面的特意留出来的PropertySourceFactory, 从命名上来看,大致就能感觉这个工厂类与属性有关了,主要就是为了创建PropertySource对象

它就比较有意思了,如果没有猜错的话,配置文件加载到Spring容器之后,多半就会与PropertySource关联起来了(所以说好的命名可以省很多注释说明)

接下来看一下这个工厂类的默认实现DefaultPropertySourceFactory,源码很简单

public class DefaultPropertySourceFactory implements PropertySourceFactory {

	@Override
	public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
		return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
	}

}
1
2
3
4
5
6
7
8

在这里我们打个断点,确认一下会发生什么神器的事情

从上面的截图可以看到,这个EncodedResource包含了我们指定的配置文件,直接单步进去,可以看到执行的时候下面这个

// org.springframework.core.io.support.ResourcePropertySource#ResourcePropertySource(org.springframework.core.io.support.EncodedResource)
public ResourcePropertySource(EncodedResource resource) throws IOException {
		super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource));
		this.resourceName = null;
}
1
2
3
4
5

请注意,核心代码不是super()这个构造方法,而是传参的PropertiesLoaderUtils.loadProperties(resource)

上面这一行调用,就是实现具体的从配置文件中获取配置信息

下面是具体的实现(摘抄有用的部分逻辑)

// org.springframework.core.io.support.PropertiesLoaderUtils
public static Properties loadProperties(EncodedResource resource) throws IOException {
	Properties props = new Properties();
	fillProperties(props, resource);
	return props;
}

public static void fillProperties(Properties props, EncodedResource resource)
		throws IOException {
  // 属性填充,注意DefaultPropertiesPersister
	fillProperties(props, resource, new DefaultPropertiesPersister());
}

static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
		throws IOException {
  ...
	try {
		String filename = resource.getResource().getFilename();
		if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
			stream = resource.getInputStream();
			// 这个是关键
			persister.loadFromXml(props, stream);
		}
		else if (resource.requiresReader()) {
			reader = resource.getReader();
			// 关键调用
			persister.load(props, reader);
		}
		else {
			stream = resource.getInputStream();
			// 关键调用
			persister.load(props, stream);
		}
	}
	...
}
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
30
31
32
33
34
35
36

配置信息的读取,最终依靠的就是org.springframework.util.DefaultPropertiesPersister#load(),到这里我们基本上就找到了从配置文件中读取配置的“幕后黑手”,直接看一下它的实现逻辑就能知道为啥不支持yaml了

public class DefaultPropertiesPersister implements PropertiesPersister {

	@Override
	public void load(Properties props, InputStream is) throws IOException {
		props.load(is);
	}

	@Override
	public void load(Properties props, Reader reader) throws IOException {
		props.load(reader);
	}
}
1
2
3
4
5
6
7
8
9
10
11
12

直接进入看到源码,非常简单直观的实现方式了,直接使用jdk的java.util.Properties#load(java.io.InputStream)来读取配置文件,所以真相已经大白了(原来都是jdk的锅😂)

# 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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

然后再我们希望使用的地方,利用自定义的工厂类替换默认的即可

@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;
}
1
2
3
4
5
6
7
8
9
10
11
12

对应的配置文件如下

biz2:
  yml:
    type: 1
    name: biz.yml.name
    ary:
      - a: hello
      - b: world
1
2
3
4
5
6
7

最后实例验证一下

@SpringBootApplication
public class Application {

    public Application(YmlProperties ymlProperties) {
        System.out.println(ymlProperties);
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class);
    }
}
1
2
3
4
5
6
7
8
9
10
11

# 3. 小结

当我们希望加载自定义的配置文件时,@PropertySource注解是一个非常好的选择(当然也可以借助多环境配置方案,指定spring.profiles.active的值,实现加载前缀为application-的配置文件,有兴趣的小伙伴可以查看我之前的博文)

请注意@PropertySource引入的配置文件不支持yaml文件,如需支持,可以参考本文中的实现方式,自定义一个yaml文件的PropertySourceFactory

最后提一句,遇到问题千万不要放过,尽量迅速解决,不要留待以后,不然拖延症发作的话,这个时间可能就一直悬着了...

# III. 其他

# 0. 项目

项目源码

  • 工程: https://github.com/liuyueyi/spring-boot-demo (opens new window)
  • 源码: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/000-properties (opens new window)

系列博文

  • 【基础系列】实现一个自定义配置加载器(应用篇) (opens new window)
  • 【基础系列】SpringBoot配置信息之默认配置 (opens new window)
  • 【基础系列】SpringBoot配置信息之配置刷新 (opens new window)
  • 【基础系列】SpringBoot基础篇配置信息之自定义配置指定与配置内引用 (opens new window)
  • 【基础系列】SpringBoot基础篇配置信息之多环境配置信息 (opens new window)
  • 【基础系列】SpringBoot基础篇配置信息之如何读取配置信息 (opens new window)

# 1. 一灰灰Blog

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

下面一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

  • 一灰灰Blog个人博客 https://blog.hhui.top (opens new window)
  • 一灰灰Blog-Spring专题博客 http://spring.hhui.top (opens new window)

一灰灰blog

编辑 (opens new window)
#PropertySource
上次更新: 2021/10/15, 19:56:22
【基础系列】实现一个自定义配置加载器(应用篇)
【基础系列】ConfigurationProperties配置绑定中那些你不知道的事情

← 【基础系列】实现一个自定义配置加载器(应用篇) 【基础系列】ConfigurationProperties配置绑定中那些你不知道的事情→

最近更新
01
【基础系列】基于maven多环境配置
04-25
02
【WEB系列】内嵌Tomcat配置Accesslog日志文件生成位置源码探索
04-24
03
【搜索系列】ES查询常用实例演示
04-18
更多文章>
Theme by Vdoing | Copyright © 2017-2022 一灰灰Blog
MIT License | 鄂ICP备18017282号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×