【问题标题】:Issue with loading multiple properties files with Spring <util:properties /> when the list of files is a parameter当文件列表是参数时,使用 Spring <util:properties /> 加载多个属性文件的问题
【发布时间】:2017-08-01 00:38:33
【问题描述】:

我们需要一起加载多个属性文件并将它们用作属性的一个来源。 &lt;util:properties&gt; 允许您传递以逗号分隔的文件列表,到目前为止一切正常。所以,以下是好的:

<util:properties loaction="conf/file1.properties,conf/file2.properties,abc-*.properties" />

但是,在我们的例子中,属性文件列表不是固定的,它来自之前加载的另一个主属性文件。我们想将该列表作为参数传递给&lt;util:properties&gt;,但它不起作用。

<util:properties location="${allPropertiesFiles}" />

${allPropertiesFiles} 定义为

allPropertiesFiles=conf/file1.properties,conf/file2.properties,abc-*.properties

由于文件列表中的逗号而失败。它将它们视为一个文件名并抛出 FileNotFoundException。

我想知道 Spring 在什么时候尝试用逗号分割文件,看起来它发生在解析 ${allPropertiesFiles} 之前。例如,如果我按照以下方式进行操作,它可以正常工作,但这对我们来说不是一个实用的解决方案,因为我们不知道该列表中包含多少文件。

<util:properties location="${propFile.location1},${propFile.location2},${propFile.location3}" />

更新:

在解析${...} 中的属性值之前,使用',' 处理和拆分似乎是一个Spring 问题。我什至尝试使用 Spring EL 来拆分它,但它在解析有效 EL 时再次失败,因为它首先根据 ',' 破坏它,然后评估表达式。下面的示例因 EL 解析异常而失败:

<util:properties location="#{'${allPropertiesFiles}'.split(',')}" />

仅供参考,此观察结果适用于 Spring 4.2.x。非常感谢任何建议。

【问题讨论】:

    标签: java spring config configuration-files properties-file


    【解决方案1】:

    所以,我找到了我的问题的解决方案/解决方法,并想在这里分享。

    util:propertiescontext:property-placeholder 的 bean 解析器将 location 属性的给定字符串值拆分为 ,。它发生在属性解析发生之前。因此,如果您想将一个以逗号分隔的属性文件列表传递给这些 bean,它将无法正常工作。

    所以,我决定不使用&lt;util:properties&gt;,而是直接使用PropertiesFactoryBean,并将位置设置为参数。这修复了 bean 定义是如何在 spring 中构建的,因为它使用 Spring 默认 bean 解析器。然后我提供了 Spring ResourceArrayPropertyEditor 的自定义版本,它将 String 转换为 Resource[]。属性编辑器在转换为Resource[] 时处理逗号分隔的字符串。

    这是我现在的上下文 xml:

        <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
            <property name="customEditors">
                <map>
                    <entry key="org.springframework.core.io.Resource[]" value="com.mycompany.CustomResourceArrayPropertyEditor"/>
                </map>
            </property>
        </bean> 
        <bean id="allPropertiesFromFiles" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
                <property name="locations" value="${propertiesFile.locations}" />
        </bean>
    

    这是我的自定义PropertyEditor

    public class CustomResourceArrayPropertyEditor extends ResourceArrayPropertyEditor
    {
      private final ResourcePatternResolver resourcePatternResolver;
    
      public CustomResourceArrayPropertyEditor()
      {
        this(new PathMatchingResourcePatternResolver());
      }
    
      public CustomResourceArrayPropertyEditor(ResourcePatternResolver resourcePatternResolver)
      {
        super(resourcePatternResolver, null);
        this.resourcePatternResolver = resourcePatternResolver; 
      }
    
      @Override
      public void setAsText(String text)
      {
        String pattern = resolvePath(text).trim();
        String[] resourceNames = StringUtils.commaDelimitedListToStringArray(pattern);
        List<Resource> resourceList = new ArrayList<Resource>();
        try {
          for (String resourceName: resourceNames)
          {
            Resource[] resources = resourcePatternResolver.getResources(resourceName);
            for (Resource res: resources)
              resourceList.add(res);
          }
        }
        catch (IOException ex) {
          throw new IllegalArgumentException("Could not resolve resource location pattern [" + pattern + "]", ex);
        }
    
        setValue(resourceList.toArray(new Resource[0]));
      }
    }
    

    我能想到的另一个解决方法是创建一个 BeanFactoryPostProcessor 来访问 bean 并更新 util:propertiescontext:propery-placeholder 的 bean 定义以简单地将 TypedStringValue 用于 locations 属性,而不是 String[] .

    希望对你有帮助。

    【讨论】:

      【解决方案2】:

      您是否尝试过用双引号将变量值括起来? 在一个小测试中,这就像一个魅力:

      应用程序上下文:

      <bean id="test" class="de.de.proptest.Test">
          <property name="p" ref="props" />
      </bean>
      <util:properties location="${props}" id="props" />
      

      我有 2 个属性文件,a.properties 的内容:

      a=1
      

      和 b.properties 与内容:

      b=2
      

      使用 JVM 参数

      -Dprops="file:/home/dominik/sandbox/proptest/a.properties, file:/home/dominik/sandbox/proptest/b.properties"
      

      我得到以下输出(切入有趣的点):

      Mar 11, 2017 1:32:11 PM org.springframework.beans.factory.config.PropertiesFactoryBean loadProperties
      INFO: Loading properties file from URL [file:/home/dominik/sandbox/proptest/a.properties]
      Mar 11, 2017 1:32:11 PM org.springframework.beans.factory.config.PropertiesFactoryBean loadProperties
      INFO: Loading properties file from URL [file:/home/dominik/sandbox/proptest/b.properties]
      Test [p={b=2, a=1}]
      

      【讨论】:

      • 您需要双引号,因为您将属性文件列表作为 jvm arg 传递,并且列表中逗号后有一个空格。我的列表来自另一个属性文件,如果我用 "" 将它括起来,它将成为属性值的一部分。
      • 第二,我尝试了您的示例,并且仅当列表作为 jvm arg 与 Spring 4.3.x 一起传递时,它才对我有效。由于列表中的逗号,早期版本的 Spring(例如 4.2.x)仍然失败。我们现在在 Spring 4.2.x 上,目前无法升级。第三,当我尝试从&lt;context:property-placeholder&gt; 加载的属性文件中传递列表时,您的示例失败。看起来 Spring 4.3.x 更好地处理 jvm arg,不知道为什么。如果我错了,请告诉我。
      猜你喜欢
      • 2012-01-23
      • 1970-01-01
      • 2013-08-21
      • 2019-03-30
      • 2019-04-16
      • 2011-08-02
      • 1970-01-01
      • 1970-01-01
      • 2016-06-18
      相关资源
      最近更新 更多