简体   繁体   English

使用Spring加载多个属性文件的问题 <util:properties /> 当文件列表是参数时

[英]Issue with loading multiple properties files with Spring <util:properties /> when the list of files is a parameter

We need to load multiple properties files together and use them as one source of properties. 我们需要将多个属性文件一起加载,并将它们用作属性的一种来源。 <util:properties> allows you to pass a comma separated list of files and everything works fine so far. <util:properties>允许您传递逗号分隔的文件列表,到目前为止一切正常。 So, the following is good: 因此,以下是好的:

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

However, in our case, the list of properties file is not fixed and it comes from another master properties file that is loaded before. 但是,在我们的情况下,属性文件列表不是固定的,它来自之前加载的另一个主属性文件。 We want to pass that list to <util:properties> as a parameter but it doesn't work. 我们想将该列表作为参数传递给<util:properties> ,但它不起作用。

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

Where ${allPropertiesFiles} is defined as ${allPropertiesFiles}定义为

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

This fails because of commas in the list of files. 由于文件列表中的逗号,此操作失败。 It treats them as one single file name and throws FileNotFoundException. 它将它们视为一个文件名,并抛出FileNotFoundException。

I wonder at what point Spring tries to split the files by comma and it looks like that it happens before resolving ${allPropertiesFiles}. 我想知道Spring在什么时候尝试用逗号分割文件,并且看起来在解决$ {allPropertiesFiles}之前就发生了。 For example if I do as below it works fine, but that is not a practical solution for us as we don't know how many files are included in that list. 例如,如果我按以下所述进行操作,则可以正常工作,但这对我们来说不是实际的解决方案,因为我们不知道该列表中包含多少个文件。

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

UPDATE: 更新:

It seems to be a Spring issue with processing and splitting with ',' before resolving the property value in ${...} . ${...}解析属性值之前,用','处理和拆分似乎是Spring问题。 I even tried using Spring EL to split it but it fails again with parsing the valid EL, because it first breaks it based on ',' then evaluates the expression. 我什至尝试使用Spring EL对其进行拆分,但由于解析有效的EL再次失败,因为它首先基于'将其中断,然后对表达式求值。 Below example fails with EL parse exception: 下面的示例因EL分析异常而失败:

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

FYI this observation is with Spring 4.2.x. 仅供参考,该观察是在Spring 4.2.x中进行的。 Any suggestion is much appreciated. 任何建议深表感谢。

Have you tried to surround your variable value with double quotes? 您是否尝试过用双引号将变量值引起来? In a small test, this worked like a charm: 在一个小的测试中,这就像一个魅力:

ApplicationContext: ApplicationContext的:

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

I have 2 properties files, a.properties with content: 我有2个属性文件,内容为a.properties:

a=1

and b.properties with content: 和b.properties内容:

b=2

With the JVM argument 带有JVM参数

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

I get the following output (cut to the interesting points): 我得到以下输出(切到有趣的地方):

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}]

So, I found a solution/workaround to my question and would like to share it here. 因此,我找到了问题的解决方案/解决方法,并希望在此处分享。

The bean parsers of util:properties and context:property-placeholder split the given string value for location property by , . 的豆解析器util:propertiescontext:property-placeholder分割为给定的字符串值location由属性, It happens way before property resolution occurs. 它发生在属性解析发生之前。 Therefore if you want to pass a comma-separated list of properties files to these beans it just won't work. 因此,如果要将逗号分隔的属性文件列表传递给这些bean,它将无法正常工作。

So, instead of using <util:properties> I decided to use PropertiesFactoryBean directly and set the location as a parameter. 因此,我决定不直接使用<util:properties> ,而是直接使用PropertiesFactoryBean并将位置设置为参数。 This fixes how the bean definition is built in spring, beacuse it uses Spring default bean parser. 这修复了bean定义在spring中的构建方式,因为它使用了Spring默认的bean解析器。 Then I provided a custom version of Spring ResourceArrayPropertyEditor which converts a String to Resource[] . 然后,我提供了Spring ResourceArrayPropertyEditor的自定义版本,该版本将String转换为Resource[] The property editor handles comma-separated strings while converting to Resource[] . 属性编辑器在转换为Resource[]时处理逗号分隔的字符串。

Here is my context xml now: 现在是我的上下文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>

And here is my custom PropertyEditor : 这是我的自定义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]));
  }
}

The other workaround that I could think of was to create a BeanFactoryPostProcessor to visit beans and update bean definitions of util:properties and context:propery-placeholder to simply use TypedStringValue for locations property as opposed to String[] . 我想到的另一种解决方法是创建一个BeanFactoryPostProcessor来访问Bean,并更新util:propertiescontext:propery-placeholder Bean定义,以将TypedStringValue用作locations属性而不是String[]

Hope it helps. 希望能帮助到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM