1: 概述
SpringBoot 的 @PropertySource 注解只支持加载 properties 结尾的文件. 当使用 @ConfigurationProperties
注解配合 @EnableConfigurationProperties 注解将配置转换为 JavaBean 时, 可能需要配合 @PropertySource
注解加载指定的配置文件. 所以为了支持以 YAML 或者 YAML 文件, 我自定义了注解 @YmlPropertySource.
2: 实现
声明注解 @YmlPropertySource
- /**
- * 类描述: load YAML 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
- * — for example, {@code "classpath:/com/myco/app.yml|yaml"}
- * <p>Resource location wildcards (e.g. **/*.YAML|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 YAML 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 将 YAML|YAML 文件转换为 properties 文件, 然后通过
实现 EnvironmentAware 接口, 将配置文件属性写入到 spring 的 Environment 环境中. 但是该实现有点
缺陷, 就是如果使用 @ConfigurationProperties 和 @EnableConfigurationProperties 将配置属性
转换为 JavaBean 时, 需要将 @YmlProperySource 注解标注到该 JavaBean 上.
来源: https://www.cnblogs.com/liuenyuan1996/p/11033080.html