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
    * &mdash; 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)的更多相关文章

  1. springboot属性类自动加载配置文件中的值

    springboot属性类自动加载配置文件中的值,如Person类加载在yml中配置的name,age等属性值,可以通过如下步骤获取: 类上添加@ConfigurationProperties注解,p ...

  2. SpringBoot 自定义注解 实现多数据源

    SpringBoot自定义注解实现多数据源 前置学习 需要了解 注解.Aop.SpringBoot整合Mybatis的使用. 数据准备 基础项目代码:https://gitee.com/J_look/ ...

  3. JVM自定义类加载器加载指定classPath下的所有class及jar

    一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...

  4. SpringBoot系列之配置文件加载位置

    SpringBoot系列之配置文件加载位置 SpringBoot启动会自动扫描如下位置的application.properties或者application.yml文件作为Springboot的默认 ...

  5. 如果你还不知道如何控制springboot中bean的加载顺序,那你一定要看此篇

    1.为什么需要控制加载顺序 springboot遵从约定大于配置的原则,极大程度的解决了配置繁琐的问题.在此基础上,又提供了spi机制,用spring.factories可以完成一个小组件的自动装配功 ...

  6. google官方的下拉刷新+自定义上拉加载更多

    转载请标注转载:http://blog.csdn.net/oqihaogongyuan/article/details/50949118 google官方的下拉刷新+自定义上拉加载更多 现在很多app ...

  7. 自定义ProgressBar的加载效果

    三种方式实现自定义圆形页面加载中效果的进度条 To get a ProgressBar in the default theme that is to be used on white/light b ...

  8. SpringBoot自带热加载开发工具

    SpringBoot自带热加载开发工具 maven配置: <!-- SpringBoot自带热加载开发工具 --> <dependency> <groupId>or ...

  9. Spring Boot自定义配置与加载

    Spring Boot自定义配置与加载 application.properties主要用来配置数据库连接.日志相关配置等.除了这些配置内容之外,还可以自定义一些配置项,如: my.config.ms ...

随机推荐

  1. VS2017十五项新功能体验

    Visual Studio 2017十五项新功能体验 Visual Studio 2017正式已经于2017.3.7号正式发布,选在这一天发布也是为了纪念Visual Studio 二十周年.MVP ...

  2. 给博客签上CC协议

    大家都知道开源软件.通过开放源代码的方式,允许用户学习.修改.增进提高这些软件质量.软件界的开源协议很多,比如常见的 Apache,BSD,GPL 等等.这是一种充分利用网络的便利性,鼓励分享和创新的 ...

  3. String方法总结

    蓝背景为与Array相同的方法 一.字符方法 charAt(index):返回在指定位置的字符. var str="abcdefg"; //undefined str[0] //& ...

  4. jquery validate 详细说明

    jQuery校验 官网地址:http://bassistance.de/jquery-plugins/jquery-plugin-validation 一导入js库 <script src=&q ...

  5. [Hibernate系列—] 3. 映射文件和使用SchemaExport制作自己主动Schema

    自己定义映射文件 这里的映射文件指的是相应到数据库表的xml 的定义文件. 相应的每一个数据库表栏位, 能够定义的属性有: 属性名 类型 Description length number 栏位的长度 ...

  6. WPF太阳、地球、月球运动轨迹模拟

    原文:WPF太阳.地球.月球运动轨迹模拟 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/yangyisen0713/article/details/ ...

  7. NPM切换源

    可以试试切换下你的NPM源.看是否能得到解决.国内的NPM有CNPM和淘宝的NPM源比较稳定.npm源切换和工具可参照站内贴 nrm工具的使用或者是直接用命令切换   npm config set r ...

  8. Parse陨落,开发者服务今后路在何方?

    Parse为开发者提供移动应用的后台服务,包括数据存储.消息推送及用户管理等等.因此方便开发者可专心在客户端的制作,简化服务器端的设计. 关于 Parse 关停 2016年1月28日,Parse 官方 ...

  9. WPF中画虚线

    原文:WPF中画虚线 在WPF中,画线的方法十分简单,只要声明一个Line然后添加到指定的位置就可以了,但Line并不仅仅只能画一条直线,还可以对直线进行修饰. 1.Line.StrokeDashAr ...

  10. WPF 4 DataGrid 控件(进阶篇一)

    原文:WPF 4 DataGrid 控件(进阶篇一)      上一篇<WPF 4 DataGrid 控件(自定义样式篇)>中,我们掌握了DataGrid 列表头.行表头.行.单元格相关的 ...