SpringBoot自定義加載yml實(shí)現(xiàn)方式,附源碼解讀
自定義加載yml,附源碼解讀
昨天在對公司的微服務(wù)配置文件標(biāo)準(zhǔn)化的過程中,發(fā)現(xiàn)將原來的properties文件轉(zhuǎn)為yml文件之后,微服務(wù)module中標(biāo)記有@Configuration的配置類都不能正常工作了,究其原因,是由于@PropertySource屬性默認(rèn)只用于標(biāo)記并告訴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 缺點(diǎn)
YAML 文件不能用 @PropertySource 注解來標(biāo)記加載。因此,在需要加載值的場景,你需要使用屬性文件。
解決方法
解決這個問題并不難,我們只需要自定義一個yaml文件加載類,并在@PropertySource注解的factory屬性中聲明就可以。scala版實(shí)現(xiàn)代碼如下,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 數(shù)據(jù)源配置
? */
@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") //設(shè)置JPA實(shí)體包路徑
? ? ? .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
? }
}源碼解讀
實(shí)現(xiàn)該功能涉及兩個地方:
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接口的實(shí)現(xiàn),在我們自定義了yaml文件加載類之后,它的實(shí)現(xiàn)關(guān)系如下:

從以上類圖可以發(fā)現(xiàn),它的實(shí)現(xiàn)類主要有2個:
DefaultPropertySourceFactory:默認(rèn)的配置文件解析類,主要用于解析properties配置文件YamlPropertyLoaderFactory:自定義的yaml資源解析類,主要用于解析yaml配置文件,使用時需要在PropertySource注解的factory屬性上聲明
這兩個類將配置文件解析后,會將屬性信息存入Spring的Environment對象中,以供我們通過@Value注解等方式使用。
因此,我們?nèi)绻龅絪pring boot不能加載并解析自定義配置的時候,可以試試自定義配置文件解析類解決。
參考鏈接
Exposing YAML as Properties in the Spring Environment
ConfigurationProperties loading list from YML:base on kotlin
Properties轉(zhuǎn)YAML idea插件——生產(chǎn)力保證:Properties to YAML Converter
如何引入多個yml方法
SpringBoot默認(rèn)加載的是application.yml文件,所以想要引入其他配置的yml文件,就要在application.yml中激活該文件
定義一個application-resources.yml文件(注意:必須以application-開頭)
application.yml中:
spring:? ?profiles: ? ?active: resources
以上操作,xml自定義文件加載完成,接下來進(jìn)行注入。
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);
? ?}
}測試結(jié)果:

以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中的線程安全集合CopyOnWriteArrayList解析
這篇文章主要介紹了Java中的線程安全CopyOnWriteArrayList解析,CopyOnWriteArrayList是ArrayList的線程安全版本,從他的名字可以推測,CopyOnWriteArrayList是在有寫操作的時候會copy一份數(shù)據(jù),然后寫完再設(shè)置成新的數(shù)據(jù),需要的朋友可以參考下2023-12-12

