好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

SpringBoot自定义加载yml实现方式,附源码解读

自定义 加载 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不能加载并解析自定义配置的时候,可以试试自定义配置文件解析类解决。

参考链接

YAML Shortcomings

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

查看更多关于SpringBoot自定义加载yml实现方式,附源码解读的详细内容...

  阅读:26次