SpringBoot自定義加載yml實(shí)現(xiàn)方式,附源碼解讀
自定義加載yml,附源碼解讀
昨天在對(duì)公司的微服務(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)記加載。因此,在需要加載值的場(chǎng)景,你需要使用屬性文件。
解決方法
解決這個(gè)問題并不難,我們只需要自定義一個(gè)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) ? } }
這個(gè)類繼承自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)該功能涉及兩個(gè)地方:
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主要用來聲明解析配置文件的類,這個(gè)類必須是PropertySourceFactory接口的實(shí)現(xiàn),在我們自定義了yaml文件加載類之后,它的實(shí)現(xiàn)關(guān)系如下:
從以上類圖可以發(fā)現(xiàn),它的實(shí)現(xiàn)類主要有2個(gè):
DefaultPropertySourceFactory
:默認(rèn)的配置文件解析類,主要用于解析properties配置文件YamlPropertyLoaderFactory
:自定義的yaml資源解析類,主要用于解析yaml配置文件,使用時(shí)需要在PropertySource注解的factory屬性上聲明
這兩個(gè)類將配置文件解析后,會(huì)將屬性信息存入Spring的Environment對(duì)象中,以供我們通過@Value注解等方式使用。
因此,我們?nèi)绻龅絪pring boot不能加載并解析自定義配置的時(shí)候,可以試試自定義配置文件解析類解決。
參考鏈接
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
如何引入多個(gè)yml方法
SpringBoot默認(rèn)加載的是application.yml文件,所以想要引入其他配置的yml文件,就要在application.yml中激活該文件
定義一個(gè)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 + '\'' + ? ? ? ? ? ? ? ?'}'; ? ?} }
測(cè)試類:
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); ? ?} }
測(cè)試結(jié)果:
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中的線程安全集合CopyOnWriteArrayList解析
這篇文章主要介紹了Java中的線程安全CopyOnWriteArrayList解析,CopyOnWriteArrayList是ArrayList的線程安全版本,從他的名字可以推測(cè),CopyOnWriteArrayList是在有寫操作的時(shí)候會(huì)copy一份數(shù)據(jù),然后寫完再設(shè)置成新的數(shù)據(jù),需要的朋友可以參考下2023-12-12