当文件列表是参数时,使用 Spring <util:properties /> 加载多个属性文件时出现问题
Issue with loading multiple properties files with Spring <util:properties /> when the list of files is a parameter
我们需要一起加载多个属性文件并将它们用作属性的一个来源。 <util:properties>
允许您传递逗号分隔的文件列表,到目前为止一切正常。所以,以下是好的:
<util:properties loaction="conf/file1.properties,conf/file2.properties,abc-*.properties" />
但是,在我们的例子中,属性文件列表不是固定的,它来自之前加载的另一个主属性文件。我们想将该列表作为参数传递给 <util:properties>
,但它不起作用。
<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 相关。非常感谢任何建议。
您是否尝试过用双引号将您的变量值括起来?
在一个小测试中,这很有效:
应用程序上下文:
<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}]
所以,我找到了一个 solution/workaround 我的问题,并想在这里分享它。
util:properties
和 context:property-placeholder
的 bean 解析器将 location
属性 的给定字符串值拆分为 ,
。它发生在 属性 解决之前。因此,如果您想将以逗号分隔的属性文件列表传递给这些 bean,那将无法正常工作。
所以,我决定直接使用 PropertiesFactoryBean
并将位置设置为参数,而不是使用 <util:properties>
。这修复了 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:properties
和 context:propery-placeholder
的 bean 定义以简单地使用 TypedStringValue
locations
属性 相对于 String[]
.
希望对您有所帮助。
我们需要一起加载多个属性文件并将它们用作属性的一个来源。 <util:properties>
允许您传递逗号分隔的文件列表,到目前为止一切正常。所以,以下是好的:
<util:properties loaction="conf/file1.properties,conf/file2.properties,abc-*.properties" />
但是,在我们的例子中,属性文件列表不是固定的,它来自之前加载的另一个主属性文件。我们想将该列表作为参数传递给 <util:properties>
,但它不起作用。
<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 相关。非常感谢任何建议。
您是否尝试过用双引号将您的变量值括起来? 在一个小测试中,这很有效:
应用程序上下文:
<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}]
所以,我找到了一个 solution/workaround 我的问题,并想在这里分享它。
util:properties
和 context:property-placeholder
的 bean 解析器将 location
属性 的给定字符串值拆分为 ,
。它发生在 属性 解决之前。因此,如果您想将以逗号分隔的属性文件列表传递给这些 bean,那将无法正常工作。
所以,我决定直接使用 PropertiesFactoryBean
并将位置设置为参数,而不是使用 <util:properties>
。这修复了 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:properties
和 context:propery-placeholder
的 bean 定义以简单地使用 TypedStringValue
locations
属性 相对于 String[]
.
希望对您有所帮助。