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. python生成测试图片

    直接代码 import cv2.cv as cv saveImagePath = 'E:/ScreenTestImages/' colorRed = [0,0,255] colorGreen = [0 ...

  2. Android GPS获取当前位置信息

    package com.example.gpstest; import org.apache.http.util.LangUtils; import android.content.Context; ...

  3. hdu 1087 Super Jumping! Jumping! Jumping!(dp 最长上升子序列和)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1087 ------------------------------------------------ ...

  4. Redis入门手册

    这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下 [本教程目录] 1.redis是什么 2.redis的作者何许人也 3.谁在使用r ...

  5. 恩布拉科业务IM 1.8 版本号,内部沟通软件

    恩布拉科业务IM,开源企业IM,免费企业即时通讯,内部沟通平台,Entboost通告v1.8版本号,主要版本更新: 管理中心添加系统监控.集群管理二大功能模块:添加云盘空间.离线消息.文件大小等參数配 ...

  6. 使用Ocelot做网关

    1首先创建一个json的配置文件,文件名随便取,我取Ocelot.json 这个配置文件有两种配置方式,第一种,手动填写 服务所在的ip和端口:第二种,用Consul进行服务发现 第一种如下: { & ...

  7. Windows10内置ubuntu子系统安装后中文环境设置

    原文:Windows10内置ubuntu子系统安装后中文环境设置 第一开启相关设置,使用小娜(Win键+c)直接查找关键字打开更快.   ①设置→查找"针对开发人员"→开发人员模式 ...

  8. NuttX 构建系统

    (嵌入式 实时操作系统 rtos nuttx 7.1 makefile) NuttX 构建系统 转载请注明出处: http://blog.csdn.net/zhumaill/article/detai ...

  9. mac在下面Apache 创 .htaccess档

    在设定一个固定的链接将提下面的例子说明样题: 若您的 .htaccess 文件可写.我们能够自己主动改动它.但似乎它不可写,因此我们在下方列出了您 .htaccess 文件里应该增加的URL 重写规则 ...

  10. ValueStack和OGNL达到Struts2形式的数据存储原理

    (1)最近学习struts相框,我们在快乐struts强大.为了便于使用转发,但不了解详细的内部数据存储: (2)网上找了很多关于struts数据存储的原理,但我还没有找到一个具体的解释,本书上找到了 ...