记一次SpringBoot2.x的@ImportResource和@PropertySource的前言中不允许有内容的坑

2022-11-03 13:11:25 浏览数 (1)

记一次SpringBoot2.x的@ImportResource和@PropertySource的坑

        众所周知,SpringBoot提倡的是无配置文件,也就摒弃了以前用Spring时繁重的.xml配置文件,取而代之的使用SpringBoot特有的@Configuration注解的JavaConfig配置类。配置方式和XML配置方式几乎一一对应,少有区别。
        那么当我们需要在SpringBoot中,需要使用XML配置文件,或者需要使用额外的.properties, .yml文件时,我们需要怎么做?
这时候就想到了几种方式:@ImportResource, @PropertySource
@ImportResource 是用于导入外部的.xml文件
@PropertySource 是用于导入变量类型的配置文件如.properties .yml
如果你用@ImportResource(locations = “xxx.yml”),通常情况你会得到这样的报错,

Caused by: org.xml.sax.SAXParseExcepton; lineNumber: 1; columnNumber: 1; 前言中不允许有内容          网上有大量关于这种错误的说法,但是绝大部分都是说你的文件编码格式有问题,需要转换。我在试过大量的方法后,还是不行,同时我也看到很多解决帖的下方留言,大多数都是留言不行,解决不了,我高度怀疑那些没质量的解决帖都是互相抄袭的。但是有一种方法我没有试,就是用notePad方式打开文件,然后保存为 UTF-8 with no BOM 的方式

         在尝试了很多次之后,我都无法通过@ImportResource的方式来导入外部.yml .properties配置文件,我也没有细心去查阅相关的源码,因此我转而关注@PropertySource来帮助我解决问题。

在使用@PropertySource的时候又发现了一个问题,它能正常帮助我导入.properties文件,我也能通过@ConfigurationProperties和@Value来得到配置文件中的相应变量值,但是它不能正确解读.yml文件。我想对于SpringBoot的常年开发者来说,早就已经把习惯改为用YML语言来书写配置文件,要换回使用properties虽然不麻烦,也有转换工具帮助,但是就不能有一种方法解决这个问题吗?

可喜可贺的是,@PropertySource里面有一个属性叫factory,默认实现类是DefaultPropertySourceFactory,这个factory的作用是解析配置文件,因此简单翻阅了DefaultPropertySourceFactory的源码,发现它对yml的支持非常鸡肋,默认是只能解析.properties文件。因此我对其做了扩展,让其在解析配置文件的时候,判断配置文件是.properties还是.yml,然后走相应的解析逻辑。
代码语言:javascript复制
/**
 * 该类是扩展{@link org.springframework.core.io.support.DefaultPropertySourceFactory}的,
 * 用于{@link org.springframework.context.annotation.PropertySource}的factory属性。
 *
 * @PropertySource的facotroy属性,默认是使用 DefaultPropertySourceFactory 实现。该实现只能解析.properties配置文件,
 * 因此.yml文件无法解析。
 *
 */
public class YmlPropertiesFactory extends DefaultPropertySourceFactory {

    @Override
    public PropertySource<?> createPropertySource(String name, EncodedResource resource)
            throws IOException {
        String sourceName = Optional.ofNullable(name).orElse(resource.getResource().getFilename());
        if (!resource.getResource().exists()) {
            // return an empty Properties
            return new PropertiesPropertySource(sourceName, new Properties());
        } else if (sourceName.endsWith(".yml") || sourceName.endsWith(".yaml")) {
            Properties propertiesFromYaml = loadYaml(resource);
            return new PropertiesPropertySource(sourceName, propertiesFromYaml);
        } else {
            return super.createPropertySource(name, resource);
        }
    }

    /**
     * load yaml file to properties
     *
     * @param resource
     * @return
     * @throws IOException
     */
    private Properties loadYaml(EncodedResource resource) throws IOException {
        YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
        factory.setResources(resource.getResource());
        factory.afterPropertiesSet();
        return factory.getObject();
    }
}
代码语言:javascript复制
@PropertySource(value = {"classpath:dy.yml"}, ignoreResourceNotFound = true, factory = YmlPropertiesFactory.class)
好了,现在你就能正常解析YML文件
小结:

(1) 如果你要导入外部XML文件,用@ImportResource (2) 如果你要导入的是.properties .yml这样的配置文件,用@PropertySource

或许我的总结是错误的,但就目前来看,这样能够解决SpringBoot导入外部配置文件时的问题,或许ImportResource也能导入.properties .yml文件,只是我方式用错了,如果有深入研究的过的朋友,可以在下方留言为社区贡献一份力量。

0 人点赞