SpringBoot自定义加载yml实现方式,附源码解读
作者:九州暮云 发布时间:2022-01-22 22:39:40
自定义加载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);
}
}
测试结果:
来源:https://my.oschina.net/dabird/blog/1795007


猜你喜欢
- 1.概述注解可以定义到方法上,类上,一个注解相当与一个类,就相当于实例了一个对象,加上了注解,就相当于加了一个标志。常用的注解:@Overr
- 之前在开发一个程序,希望能够通过属性名称读取出属性值,但是由于那时候不熟悉反射,所以并没有找到合适的方法,做了不少的重复性工作啊!然后今天我
- 本文实例为大家分享了Unity实现本地文本多语言化的具体代码,供大家参考,具体内容如下在unity项目过程中大多都会遇到多语言化,下面讲一下
- 我们可以试想ImageView能显示图片,而VideoView就是用来显示视频的。使用VideoView播放视频的步骤如下 【1】在界面布局
- Mybatis批量插入index out of range错误往往我们看到网上关于各类关于批量插入报这种错误的文章都是传入的集合为null,
- 从Map、JSONObject取不存在键值对时异常1.在Map中取不存在的键值对时不会报异常只会返回null@Test  
- 因为在准备讲Maven用Maven Helper插件的时候,在网上学习,发现资料很少,我就把自己研究的配置分享给大家!!IDEA(本人用的2
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项
- android电话管理器(TelephonyManger)实例:TelephonyManger是管理电话状态、网络信息的服务类。添加权限:&
- 前言复习一下spring实现IOC的源码流程准备工作:强烈建议大家从git上拉取spring源码来学习Spring源码。因为里面相较于IDE
- 为什么需要互斥量在多任务操作系统中,同时运行的多个任务可能都需要使用同一种资源。这个过程有点类似于,公司部门里,我在使用着打印机打印东西的同
- 在做android图片加载的时候,由于手机屏幕受限,很多大图加载过来的时候,我们要求等比例缩放,比如按照固定的宽度,等比例缩放高度,使得图片
- 很多时候木马程序会伪装成其他格式的文件上传到网站,最常见的如图片格式。本文就以C#为例讲述C#判断上传文件是否是图片以防止木马上传的方法,具
- 简单介绍快速排序(Quicksort) 是对 冒泡排序的一种改进。基本思想快速排序算法通过多次比较和交换来实现排序,其排序流程如下:(1)首
- 前言:1.最近项目上在测试人员压测过程中发现了OOM问题,项目使用springboot搭建项目工程,通过查看日志中包含信息:unable t
- 一般而言,Android 应用在请求数据时都是以 Get 或 Post 等方式向远程服务器发起请求,那你有没有想过其实我们也可以在 Andr
- 获取sdcard目录 public static String getSDPath() { File sdDir
- 一、说明到目前为止介绍的功能共享一对一的关系:即一个进程发送和一个进程接收。链接是通过标签建立的。本节介绍在多个进程中调用相同参数但执行不同
- 简介在上一篇文章中,我们列举了flutter中的所有layout类,并且详细介绍了两个非常常用的layout:Row和Column。掌握了上
- 自定义View一直是横在Android开发者面前的一道坎。一、View和ViewGroup的关系从View和ViewGroup的关系来看,V