SpringBoot自定义注解@YmlPropertySource加载yml或者yaml文件

1:概述

SpringBoot的@PropertySource注解只支持加载 properties结尾的文件。当使用@ConfigurationProperties

注解配合@EnableConfigurationProperties注解将配置转换为JavaBean时,可能需要配合@PropertySource

注解加载指定的配置文件。所以为了支持以yml或者yaml文件,我自定义了注解@YmlPropertySource。

2:实现

声明注解@YmlPropertySource

/**
 * 类描述: load yml or yaml file into {@link org.springframework.core.env.Environment}
 *
 * @author liuenyuan
 * @date 2019/6/16 20:12
 * @describe
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface YmlPropertySource {

    /**
     * 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.
     * 

Both traditional and XML-based properties file formats are supported * — for example, {@code "classpath:/com/myco/app.yml|yaml"} *

Resource location wildcards (e.g. **/*.yml|yaml) are not permitted; * each location must evaluate to exactly one {@code .properties} resource. *

${...} placeholders will be resolved against any/all property sources already * registered with the {@code Environment}. See {@linkplain YmlPropertySource above} * for examples. *

Each location will be added to the enclosing {@code Environment} as its own * property source, and in the order declared. */ String[] 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 YmlPropertySource} bean post processor.this class convert the yml or yaml file
 * {@link YmlPropertySource#value()} to {@link PropertiesPropertySource},and add the property source
 * named {@link YmlPropertySource#name()} into {@link Environment}.When you use this annotation,you
 * must for follow example:
 * 
{@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;
 * }}
* * @author liuenyuan * @date 2019/6/16 20:13 * @describe * @see YmlPropertySource * @see InstantiationAwareBeanPostProcessorAdapter * @see EnvironmentAware * @see ResourceLoaderAware */ @Slf4j @Configuration @Order(Ordered.HIGHEST_PRECEDENCE) public class YmlPropertySourceAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements EnvironmentAware, ResourceLoaderAware { private Environment environment; private ResourceLoader resourceLoader; @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 { YmlPropertySource ymlPropertySource = AnnotationUtils.findAnnotation(bean.getClass(), YmlPropertySource.class); Map> ymlPropertySourceMap = new LinkedHashMap<>(); if (ymlPropertySource != null) { String[] value = ymlPropertySource.value(); String name = ymlPropertySource.name(); List resources = new ArrayList<>(); Arrays.stream(value).forEach(location -> { Resource resource = resourceLoader.getResource(location); try { if (resource.getInputStream() != null) { resources.add(resource); } } catch (IOException e) { log.warn("file {} not found.", location); } }); ymlPropertySourceMap.put(name, resources); } if (!ymlPropertySourceMap.isEmpty()) { ymlPropertySourceMap.forEach((name, resources) -> { YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean(); yamlPropertiesFactoryBean.setResources(resources.toArray(new Resource[resources.size()])); PropertiesPropertySource propertiesPropertySource = new PropertiesPropertySource(name, yamlPropertiesFactoryBean.getObject()); ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) environment; MutablePropertySources propertySources = configurableEnvironment.getPropertySources(); propertySources.addLast(propertiesPropertySource); }); } return true; } }

想法

使用InstantiationAwareBeanPostProcessorAdapter的postProcessAfterInstantiation(Object bean, String beanName)方法,然后通过YamlPropertiesFactoryBean将yml|yaml文件转换为properties文件,然后通过

实现EnvironmentAware接口,将配置文件属性写入到spring的Environment环境中。但是该实现有点

缺陷,就是如果使用@ConfigurationProperties和@EnableConfigurationProperties将配置属性

转换为JavaBean时,需要将@YmlProperySource注解标

你可能感兴趣的:(springboot)