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. 最简单的基于JavaEE和FFmpeg的视频网站

    最简单的视频网站 Simplest Video Website 雷霄骅 Lei Xiaohua leixiaohua1020@126.com 中国传媒大学/数字电视技术 Communication U ...

  2. Delphi Base64编码/解码及ZLib压缩/解压

    最近在写的程序与SOAP相关,所以用到了一些Base64编码/解码及数据压缩/解压方面的知识. 在这里来作一些总结:   一.Base64编码/解码   一般用到的是Delphi自带的单元EncdDe ...

  3. Android之消息推送实现

    在开发Android和iPhone应用程序时,我们往往需要从服务器不定的向手机客户端即时推送各种通知消息,iPhone上已经有了比较简单的和完美的推送通知解决方案,可是Android平台上实现起来却相 ...

  4. Android 混淆[学习笔记]

    Android 混淆 Gradle的简介: http://www.flysnow.org/2015/03/30/manage-your-android-project-with-gradle.html ...

  5. Cocos2d-X之LUA注意事项

    「使用计时器」: 计时器函数原型:unsigned int scheduleScriptFunc(unsigned int handler, float interval, bool paused) ...

  6. 简明Python3教程 19.附录 FLOSS

    FLOSS Free/Libre and Open Source Software, in short, FLOSS is based on the concept of a community, w ...

  7. 新浪微博API OAuth1 Python3客户端

    #!/usr/local/bin/python3 # coding=gbk # http://www.cnblogs.com/txw1958/ # import os, io, sys, re, ti ...

  8. mac 端安装JAVA开发环境

    一.maven安装 下载地址   https://maven.apache.org/download.cgi 下载 apache-maven-3.5.2-bin-zip 将下载的文件放在某路径下 修改 ...

  9. SICP 1.29-1.33

    1.29 (define (sum term a next b) (if (> a b) (+ (term a) (sum term (next a) next b)))) (define (c ...

  10. LeetCode - 4 - Longest Substring Without Repeating Characters

    题目 URL:https://leetcode.com/problems/median-of-two-sorted-arrays/ 解法 二分法. 总的思想是将 2 个数组用 2 个指针“整体”二分. ...