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.
     * <p>Both traditional and XML-based properties file formats are supported
     * &mdash; for example, {@code "classpath:/com/myco/app.yml|yaml"}
     * <p>Resource location wildcards (e.g. *&#42;/*.yml|yaml) 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 YmlPropertySource 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();
}

具體實現如下

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:
 * <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 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<String, List<Resource>> ymlPropertySourceMap = new LinkedHashMap<>();
        if (ymlPropertySource != null) {
            String[] value = ymlPropertySource.value();
            String name = ymlPropertySource.name();
            List<Resource> 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註解標

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章