Spring Boot自定义加载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文件资源加载类

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注解并使用

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注解:用于声明和配置自定义配置类需要加载的配置文件信息,源码及属性解释如下:

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏泰斗贤若如

不会框架不要紧,我带你自定义框架

前言:这标题说的有点大了,当一回标题党,之前在学JSP的时候提到了JSTL和EL表达式,由于一直钟情于Servlet,迟迟没有更新别的,这回算是跳出来了。这回放...

7220
来自专栏架构探险之道

[JVM] Java 内存区域与内存溢出异常

由于多线程的切换时通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(内核)都只会执行一条线程中的指令。为了线程切换后能恢复到...

12520
来自专栏木溪知识加油站

笔记——JVM、DVM(dalvik)和ART之间的区别(二十)

JVM本质上就是一个软件,是计算机硬件的一层软件抽象,在这之上才能够运行Java程序,JAVA在编译后会生成类似于汇编语言的.class字节码文件,与C语言编译...

17620
来自专栏软件开发-青出于蓝

Springboot之ConfigFileApplicationListener

                                                                                ...

10320
来自专栏happyJared

同步与异步,阻塞和非阻塞

Java 中的 BIO、NIO 和 AIO 可以理解为是 Java 语言对操作系统的各种 IO 模型的封装。在使用这些 API 的时候,不需要关心操作系统层面的...

10620
来自专栏咻咻ing

3. JanusGraph快速开始

本节将使用Gods图作为演示示例,此图在JanusGraph演示中广泛使用。该图如下图所示。这个抽象的数据模型对应图模型中的属性,这个特定的实例描述了罗马万神殿...

34420
来自专栏微信公众号:Java团长

阅读《代码整洁之道》总结

很早就阅读过《代码整洁之道》(英文版Clean Code),当时博主是个青涩的菜鸟,正在为团队创造着混乱的代码。多年的工作中,屡次被别人的代码坑的苦不堪言,回想...

11140
来自专栏老九学堂

程序员怎么追女生?用代码呀~

不管是Java,PHP还是C… ---------------- 不过话说程序员浪漫起来真的好帅!好帅!好帅!

59130
来自专栏医学生物信息学

R语言基础教程——第7章:面向对象编程(S3类)

面向对象是一种对现实世界理解和抽象的方法,当代码复杂度增加难以维护的时候,面向对象就会显得非常重要。学过Java和Javascript两种语言的话,不难理解面向...

16420
来自专栏码匠的流水账

聊聊sharding-jdbc的MasterSlaveRouter

incubator-shardingsphere-4.0.0-RC1/sharding-core/sharding-core-route/src/main/ja...

8720

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励