自定义加载yml,附源码解读
昨天在对公司的微服务配置文件标准化的过程中,发现将原来的properties文件转为yml文件之后,微服务module中标记有@Configuration的配置类都不能正常工作了,究其原因,是由于@PropertySource属性默认只用于标记并告诉spring boot加载properties类型的文件
spring boot 2.0.0.RELEASE版的文档解释如下:
24.6.4 YAML Shortcomings
YAML files cannot be loaded by using the @PropertySource annotation. So, in the case that you need to load values that way, you need to use a properties file.
这段话的意思是说:
24.6.4 YAML 缺点
YAML 文件不能用 @PropertySource 注解来标记加载。因此,在需要加载值的场景,你需要使用属性文件。
解决方法
解决这个问题并不难,我们只需要自定义一个yaml文件加载类,并在@PropertySource注解的factory属性中声明就可以。scala版实现代码如下,spring boot版本为2.0.0.RELEASE:
1、自定义yaml文件资源加载类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import org.springframework.boot.env.YamlPropertySourceLoader import org.springframework.core.env.PropertySource import org.springframework.core.io.support.{DefaultPropertySourceFactory, EncodedResource} /** * yaml资源加载类 */ class YamlPropertyLoaderFactory extends DefaultPropertySourceFactory{ override def createPropertySource(name: String, resource: EncodedResource): PropertySource[_] = { if (resource == null ) { super .createPropertySource(name, resource) } return new YamlPropertySourceLoader().load(resource.getResource.getFilename, resource.getResource, null ) } } |
这个类继承自DefaultPropertySourceFactory类,并重写了createPropertySource方法。
2、引入@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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
import com.core.conf.YamlPropertyLoaderFactory import javax.persistence.EntityManagerFactory import javax.sql.DataSource import org.springframework.beans.factory.annotation.{Autowired, Qualifier} import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder import org.springframework.boot.context.properties.ConfigurationProperties import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder import org.springframework.context.annotation.{Bean, Configuration, PropertySource} import org.springframework.core.env.Environment import org.springframework.data.jpa.repository.config.EnableJpaRepositories import org.springframework.orm.jpa.{JpaTransactionManager, LocalContainerEntityManagerFactoryBean} import org.springframework.transaction.PlatformTransactionManager /** * JPA 数据源配置 */ @Configuration @PropertySource (value = Array( "classpath:/bootstrap-report.yml" ), factory = classOf[YamlPropertyLoaderFactory]) @EnableJpaRepositories ( entityManagerFactoryRef = "reportEntityManager" , transactionManagerRef = "reportTransactionManager" , basePackages = Array( "com.report.dao" ) ) class ReportDBConfig { @Autowired private var env: Environment = _ @Bean @ConfigurationProperties (prefix = "spring.datasource.report" ) def reportDataSource(): DataSource = DataSourceBuilder.create.build @Bean (name = Array( "reportEntityManager" )) def reportEntityManagerFactory(builder: EntityManagerFactoryBuilder): LocalContainerEntityManagerFactoryBean = { val entityManager = builder .dataSource(reportDataSource()) .packages( "com.report.model" ) //设置JPA实体包路径 .persistenceUnit( "reportPU" ) .build entityManager.setJpaProperties(additionalProperties()) entityManager } @Bean (name = Array( "reportTransactionManager" )) def reportTransactionManager( @Qualifier ( "reportEntityManager" ) entityManagerFactory: EntityManagerFactory): PlatformTransactionManager = { new JpaTransactionManager(entityManagerFactory) } /** * 获取JPA配置 * * @return */ def additionalProperties(): Properties = { val properties = new Properties(); properties.setProperty( "hibernate.hbm2ddl.auto" , env.getProperty( "spring.jpa.report.hibernate.ddl-auto" )) properties.setProperty( "hibernate.show_sql" , env.getProperty( "spring.jpa.report.show-sql" )) properties.setProperty( "hibernate.dialect" , env.getProperty( "spring.jpa.report.database-platform" )) properties } } |
源码解读
实现该功能涉及两个地方:
1、@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
|
package org.springframework.context.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.io.support.PropertySourceFactory; @Target (ElementType.TYPE) @Retention (RetentionPolicy.RUNTIME) @Documented @Repeatable (PropertySources. class ) public @interface PropertySource { // 用于声明属性源名称 String name() default "" ; // 声明属性文件位置 String[] value(); // 是否忽略未找到的资源 boolean ignoreResourceNotFound() default false ; // 声明配置文件的编码 String encoding() default "" ; // 声明解析配置文件的类 Class<? extends PropertySourceFactory> factory() default PropertySourceFactory. class ; } |
2、spring boot配置文件解析类:
在@PropertySource注解的定义中,属性factory主要用来声明解析配置文件的类,这个类必须是PropertySourceFactory接口的实现,在我们自定义了yaml文件加载类之后,它的实现关系如下:
从以上类图可以发现,它的实现类主要有2个:
-
DefaultPropertySourceFactory
:默认的配置文件解析类,主要用于解析properties配置文件 -
YamlPropertyLoaderFactory
:自定义的yaml资源解析类,主要用于解析yaml配置文件,使用时需要在PropertySource注解的factory属性上声明
这两个类将配置文件解析后,会将属性信息存入Spring的Environment对象中,以供我们通过@Value注解等方式使用。
因此,我们如果遇到spring boot不能加载并解析自定义配置的时候,可以试试自定义配置文件解析类解决。
参考链接
Exposing YAML as Properties in the Spring Environment
ConfigurationProperties loading list from YML:base on kotlin
Properties转YAML idea插件——生产力保证:Properties to YAML Converter
如何引入多个yml方法
SpringBoot默认加载的是application.yml文件,所以想要引入其他配置的yml文件,就要在application.yml中激活该文件
定义一个application-resources.yml文件(注意:必须以application-开头)
application.yml中:
1
2
3
|
spring: profiles: active: resources |
以上操作,xml自定义文件加载完成,接下来进行注入。
application-resources.yml配置文件代码:
1
2
3
4
5
|
user: filepath: 12346 uname: "13" admin: aname: 26 |
方案一:无前缀,使用@Value注解
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
|
@Component //@ConfigurationProperties(prefix = "user") public class User { @Value ( "${user.filepath}" ) private String filepath; @Value ( "${user.uname}" ) private String uname; public String getFilepath() { return filepath; } public void setFilepath(String filepath) { this .filepath = filepath; } public String getUname() { return uname; } public void setUname(String uname) { this .uname = uname; } @Override public String toString() { return "User{" + "filepath='" + filepath + '\ '' + ", uname='" + uname + '\ '' + '}' ; } } |
方案二:有前缀,无需@Value注解
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
|
@Component @ConfigurationProperties (prefix = "user" ) public class User { //@Value("${user.filepath}") private String filepath; //@Value("${user.uname}") private String uname; public String getFilepath() { return filepath; } public void setFilepath(String filepath) { this .filepath = filepath; } public String getUname() { return uname; } public void setUname(String uname) { this .uname = uname; } @Override public String toString() { return "User{" + "filepath='" + filepath + '\ '' + ", uname='" + uname + '\ '' + '}' ; } } |
测试类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package com.sun123.springboot; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @RunWith (SpringRunner. class ) @SpringBootTest public class UTest { @Autowired User user; @Test public void test01(){ System.out.println(user); } } |
测试结果:
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。
原文链接:https://my.oschina.net/dabird/blog/1795007