2019独角兽企业重金招聘Python工程师标准>>>
背景
昨天在对公司的微服务配置文件标准化的过程中,发现将原来的properties文件转为yml文件之后,微服务module中标记有@Configuration的配置类都不能正常工作了,究其原因,是由于@PropertySource属性默认只用于标记并告诉spring boot加载properties类型的文件,spring boot 2.0.0.RELEASE版的文档解释如下:
24.6.4 YAML ShortcomingsYAML 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 {@Autowiredprivate 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").buildentityManager.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