SpringBoot自定义注解@YamlPropertySource加载yml或者yaml文件(扩展了@PropertySource)
1:概述
SpringBoot的@PropertySource注解只支持加载 properties结尾的文件。当使用@ConfigurationProperties
注解配合@EnableConfigurationProperties注解将配置转换为JavaBean时,可能需要配合@PropertySource
注解加载指定的配置文件。所以为了支持以yml或者yaml文件,我自定义了注解@YamlPropertySource。
2:实现
声明注解@YamlPropertySource
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.support.PropertySourceFactory;
import java.lang.annotation.*;
/**
* yaml property source and extension {@link PropertySource}
*
* @author liuenyuan
* @see org.springframework.context.annotation.PropertySource
**/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(YamlPropertySources.class)
public @interface YamlPropertySource {
/**
* Indicate the name of this property source. If omitted, a name will
* be generated based on the description of the underlying resource.
*
* @see org.springframework.core.env.PropertySource#getName()
* @see org.springframework.core.io.Resource#getDescription()
*/
String name() default "";
/**
* Indicate the resource location(s) of the properties file to be loaded.
* <p>Both traditional and XML-based properties file formats are supported
* — for example, {@code "classpath:/com/myco/app.properties"}
* or {@code "file:/path/to/file.xml"}.
* <p>Resource location wildcards (e.g. **/*.properties) are not permitted;
* each location must evaluate to exactly one {@code .properties} resource.
* <p>${...} placeholders will be resolved against any/all property sources already
* registered with the {@code Environment}. See {@linkplain PropertySource above}
* for examples.
* <p>Each location will be added to the enclosing {@code Environment} as its own
* property source, and in the order declared.
*/
String[] value();
/**
* Indicate if failure to find the a {@link #value() property resource} should be
* ignored.
* <p>{@code true} is appropriate if the properties file is completely optional.
* Default is {@code false}.
*
* @since 4.0
*/
boolean ignoreResourceNotFound() default false;
/**
* A specific character encoding for the given resources, e.g. "UTF-8".
*
* @since 4.3
*/
String encoding() default "";
/**
* Specify a custom {@link PropertySourceFactory}, if any.
* <p>By default, a default factory for standard resource files will be used.
*
* @see org.springframework.core.io.support.DefaultPropertySourceFactory
* @see org.springframework.core.io.support.ResourcePropertySource
* @since 4.3
*/
Class<? extends PropertySourceFactory> factory() default YamlPropertySourceFactory.class;
}
/**
* @author liuenyuan
* @see YamlPropertySource
**/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YamlPropertySources {
YamlPropertySource[] value();
}
具体实现如下
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.*;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import java.io.IOException;
import java.util.*;
/**
* 类描述: {@link YamlPropertySource} bean post processor.this class convert the yml or yaml file {@link YamlPropertySource#value()} to {@link PropertiesPropertySource},and add the property source
* named {@link YamlPropertySource#name()} into {@link Environment}.When you use this annotation,you
* must for follow example:
* <pre>{@code
* @link @ConfigurationProperties(prefix = "person")
* @link @YmlPropertySource(value = {"classpath:/hello.yml"}, name = "hello")
* @link @Data
* public class PersonProperties {
*
* private String name;
*
* private Integer age;
*
* private String school;
* }}</pre>
*
* @author liuenyuan
* @date 2019/6/16 20:13
* @describe
* @see YamlPropertySource
* @see InstantiationAwareBeanPostProcessorAdapter
* @see EnvironmentAware
* @see ResourceLoaderAware
*/
@Slf4j
@Configuration(value = YamlPropertySourceAnnotationPostProcessor.BEAN_NAME)
@Order(Ordered.HIGHEST_PRECEDENCE)
public class YamlPropertySourceAnnotationPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements EnvironmentAware, ResourceLoaderAware {
public final static String BEAN_NAME = "yamlPropertySourceAnnotationPostProcessor";
private Environment environment;
private ResourceLoader resourceLoader;
private final List<String> propertySourceNames = new ArrayList<>();
private static final PropertySourceFactory DEFAULT_PROPERTY_SOURCE_FACTORY = new YamlPropertySourceFactory();
@Override
public void setEnvironment(Environment environment) {
Assert.isInstanceOf(ConfigurableEnvironment.class, environment, "environment must be instance of ConfigurableEnvironment.");
this.environment = environment;
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
// Process any @PropertySource annotations
Set<YamlPropertySource> yamlPropertySources = AnnotationUtils.getRepeatableAnnotations(bean.getClass(),
YamlPropertySource.class, YamlPropertySources.class);
if (!yamlPropertySources.isEmpty()) {
Set<AnnotationAttributes> attributesSet = new LinkedHashSet<>(yamlPropertySources.size());
for (YamlPropertySource yamlPropertySource : yamlPropertySources) {
AnnotationAttributes attributes = AnnotationUtils.getAnnotationAttributes(bean.getClass(),
yamlPropertySource);
attributesSet.add(attributes);
}
for (AnnotationAttributes propertySource : attributesSet) {
if (this.environment instanceof ConfigurableEnvironment) {
try {
processPropertySource(propertySource);
} catch (IOException e) {
log.warn("exception message: {}", e.getMessage());
}
} else {
log.warn("Ignoring @YamlPropertySource annotation on [" + bean.getClass() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
}
return true;
}
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @YamlPropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
Resource resource = this.resourceLoader.getResource(resolvedLocation);
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
} catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
if (log.isInfoEnabled()) {
log.info("Properties or Yml or Yaml location [" + location + "] not resolvable: " + ex.getMessage());
}
} else {
throw ex;
}
}
}
}
private void addPropertySource(PropertySource<?> propertySource) {
String name = propertySource.getName();
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
if (this.propertySourceNames.contains(name)) {
// We've already added a version, we need to extend it
PropertySource<?> existing = propertySources.get(name);
if (existing != null) {
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
if (existing instanceof CompositePropertySource) {
((CompositePropertySource) existing).addFirstPropertySource(newSource);
} else {
if (existing instanceof ResourcePropertySource) {
existing = ((ResourcePropertySource) existing).withResourceName();
}
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
propertySources.replace(name, composite);
}
return;
}
}
if (this.propertySourceNames.isEmpty()) {
propertySources.addLast(propertySource);
} else {
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
propertySources.addBefore(firstProcessed, propertySource);
}
this.propertySourceNames.add(name);
}
}
想法
使用InstantiationAwareBeanPostProcessorAdapter的postProcessAfterInstantiation(Object bean, String beanName)方法,然后通过YamlPropertiesFactoryBean将yml|yaml文件转换为properties文件,然后通过
实现EnvironmentAware接口,将配置文件属性写入到spring的Environment环境中。但是该实现有点
缺陷,就是如果使用@ConfigurationProperties和@EnableConfigurationProperties将配置属性
转换为JavaBean时,需要将@YamlProperySource注解标注到该JavaBean上。因为我无法在Bean实例化之前获取Bean所有的Bean信息。
SpringBoot自定义注解@YamlPropertySource加载yml或者yaml文件(扩展了@PropertySource)的更多相关文章
- springboot属性类自动加载配置文件中的值
springboot属性类自动加载配置文件中的值,如Person类加载在yml中配置的name,age等属性值,可以通过如下步骤获取: 类上添加@ConfigurationProperties注解,p ...
- SpringBoot 自定义注解 实现多数据源
SpringBoot自定义注解实现多数据源 前置学习 需要了解 注解.Aop.SpringBoot整合Mybatis的使用. 数据准备 基础项目代码:https://gitee.com/J_look/ ...
- JVM自定义类加载器加载指定classPath下的所有class及jar
一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...
- SpringBoot系列之配置文件加载位置
SpringBoot系列之配置文件加载位置 SpringBoot启动会自动扫描如下位置的application.properties或者application.yml文件作为Springboot的默认 ...
- 如果你还不知道如何控制springboot中bean的加载顺序,那你一定要看此篇
1.为什么需要控制加载顺序 springboot遵从约定大于配置的原则,极大程度的解决了配置繁琐的问题.在此基础上,又提供了spi机制,用spring.factories可以完成一个小组件的自动装配功 ...
- google官方的下拉刷新+自定义上拉加载更多
转载请标注转载:http://blog.csdn.net/oqihaogongyuan/article/details/50949118 google官方的下拉刷新+自定义上拉加载更多 现在很多app ...
- 自定义ProgressBar的加载效果
三种方式实现自定义圆形页面加载中效果的进度条 To get a ProgressBar in the default theme that is to be used on white/light b ...
- SpringBoot自带热加载开发工具
SpringBoot自带热加载开发工具 maven配置: <!-- SpringBoot自带热加载开发工具 --> <dependency> <groupId>or ...
- Spring Boot自定义配置与加载
Spring Boot自定义配置与加载 application.properties主要用来配置数据库连接.日志相关配置等.除了这些配置内容之外,还可以自定义一些配置项,如: my.config.ms ...
随机推荐
- elasticsearch-jdbc
jprante/elasticsearch-jdbc The Java Database Connection (JDBC) importer allows to fetch data from JD ...
- 如何将字段中带逗号的SQLite数据库数据导入到MySQL
以前在数据库导入中没有遇到过什么问题,如下这样导入 load data local infile 'D:\data.csv' into table table1 fields terminated b ...
- Windows下如何采用微软的Caffe配置Faster R-CNN
前言 比较简单的一篇博客.https://github.com/microsoft/caffe 微软的Caffe以在Windows下编译简单而受到了很多人的喜爱(包括我),只用改改prop配置然后无脑 ...
- [Linux] ssh秘钥对免密码登陆
准备两台linux服务器 a和b , 在a上使用ssh命令登陆b服务器 , 并且不用 输入密码 1.在a服务器上,比如是root用户 ,进去/root/.ssh目录 ,没有就创建, 就是进入家目录的. ...
- Coremicro Reconfigurable Embedded Smart Sensor Node
A Coremicro Reconfigurable Embedded Smart Sensor Node has the capability of hosting intelligent algo ...
- Openstack+Kubernetes+Docker微服务
Openstack+Kubernetes+Docker微服务 渐入佳境,我们开始比较具体的工作,由于Docker是一个基础组件,所以本文的主题是Docker和Registry2. 底层系统基于Cent ...
- wpf Storyboard 不存在可解析名称“ ”的适用名称领域 No applicable name scope exists to resolve the name
原文:wpf Storyboard 不存在可解析名称“ ”的适用名称领域 No applicable name scope exists to resolve the name 写了一个 Storyb ...
- sql like N'%...%' 在C#里的写法
StringBuilder sb = new StringBuilder(); List<SqlParameter> parameters =new List<SqlParamete ...
- DB First EF中的存储过程、函数、视图
视图约等于表(属性)存储过程变为方法,方法中调用存储过程 EF可以调用存储过程,DB First的流程是刷新模型,获取存储过程,调用参考:http://blog.csdn.net/sudazf/art ...
- jquery模拟飞秋
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...