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

 更新时间:2022年03月24日 09:47:55   作者:九州暮云  
这篇文章主要介绍了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文件资源加载类 

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

如何引入多个yml方法

SpringBoot默认加载的是application.yml文件,所以想要引入其他配置的yml文件,就要在application.yml中激活该文件

定义一个application-resources.yml文件(注意:必须以application-开头)

application.yml中:

spring: 
 profiles:
   active: resources

以上操作,xml自定义文件加载完成,接下来进行注入。

application-resources.yml配置文件代码:

user:
 filepath: 12346
 uname: "13"
admin:
 aname: 26

方案一:无前缀,使用@Value注解

@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注解

@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 + '\'' +
               '}';
   }
}

测试类:

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);
   }
}

测试结果:

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 浅谈Java中File文件的创建以及读写

    浅谈Java中File文件的创建以及读写

    文中有非常详细的步骤介绍了Java中file文件的创建以及读写,对刚开始学习java的小伙伴们很有帮助,而且下文有非常详细的代码示例及注释哦,需要的朋友可以参考下
    2021-05-05
  • 浅谈Java中BIO、NIO和AIO的区别和应用场景

    浅谈Java中BIO、NIO和AIO的区别和应用场景

    这篇文章主要介绍了Java中BIO、NIO和AIO的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Java如何利用Socket进行数据读写

    Java如何利用Socket进行数据读写

    这篇文章主要介绍了Java如何利用Socket进行数据读写,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Spring Cloud如何使用Feign构造多参数的请求

    Spring Cloud如何使用Feign构造多参数的请求

    这篇文章主要介绍了Spring Cloud如何使用Feign构造多参数的请求,以GET以及POST方法的请求为例进行讲解,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • SpringBoot实现登录拦截的示例代码

    SpringBoot实现登录拦截的示例代码

    如果我们不进行登录拦截的话,即使我们跳过登录页面直接去访问任意一个页面也能访问成功,那么登录功能就没有意义,同时也会存在安全问题,本文就来介绍一下SpringBoot登录拦截,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • Java8的Optional如何干掉空指针(示例详解)

    Java8的Optional如何干掉空指针(示例详解)

    这篇文章主要介绍了Java8的Optional如何干掉空指针,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-01-01
  • Maven如何构建可执行的jar包(包含依赖jar包)

    Maven如何构建可执行的jar包(包含依赖jar包)

    这篇文章主要介绍了Maven如何构建可执行的jar包(包含依赖jar包) ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • java网络通信技术之简单聊天小程序

    java网络通信技术之简单聊天小程序

    这篇文章主要为大家详细介绍了java网络通信技术之简单聊天小程序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-07-07
  • 解决Nacos集群启动失败:java版本问题

    解决Nacos集群启动失败:java版本问题

    这篇文章主要介绍了解决Nacos集群启动失败:java版本问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • 使用log4j2自定义配置文件位置和文件名(附log4j2.xml配置实例)

    使用log4j2自定义配置文件位置和文件名(附log4j2.xml配置实例)

    这篇文章主要介绍了使用log4j2自定义配置文件位置和文件名(附log4j2.xml配置实例),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12

最新评论