正确使用 Spring 环境配置文件来管理 PropertySourcesPlaceholderConfigurer 和属性文件集
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23618518/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Correctly using Spring environment profiles in order to manage PropertySourcesPlaceholderConfigurer and sets of properties files
提问by balteo
I am working on a Spring application and I am realizing that I have an issue with the way I manage my properties. I use Spring environment profiles in order to load my properties and I've recently added more profiles which has made my properties files unmanagable.
我正在开发一个 Spring 应用程序,我意识到我管理我的属性的方式有问题。我使用 Spring 环境配置文件来加载我的属性,并且我最近添加了更多配置文件,这使我的属性文件无法管理。
The properties files are located in different folders within src/main/resources/META-INF/props/
, with eah folder matching a different Spring environment profile.
属性文件位于 内的不同文件夹中src/main/resources/META-INF/props/
,每个文件夹都匹配不同的 Spring 环境配置文件。
I have at least 5 profiles now which means I have 5 sub-folders each containing the properties files with the same names but with different values for only some keys.
我现在至少有 5 个配置文件,这意味着我有 5 个子文件夹,每个子文件夹都包含名称相同但仅某些键具有不同值的属性文件。
Here is how it looks:
这是它的外观:
Here is how I've configured my PropertyPlaceholders:
这是我如何配置我的 PropertyPlaceholders:
@Configuration
public class PropertyPlaceholderConfiguration {
@Profile(Profiles.CLOUD)
static class cloudConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/cloud/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.DEFAULT)
static class defaultConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/default/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.TEST)
static class testConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/test/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
}
@Profile(Profiles.DEV)
static class devConfiguration {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/dev/*.properties"));
return propertySourcesPlaceholderConfigurer;
}
...
}
To sum up, my problem is as follows:
总结一下,我的问题如下:
- key/value pairs are duplicated all over the 5 different folders because only a few values are different.
- 键/值对在 5 个不同的文件夹中重复,因为只有少数值不同。
I am therefore looking for a new strategy to manage the differences between the different environments.
因此,我正在寻找一种新策略来管理不同环境之间的差异。
Can anyone please help?
有人可以帮忙吗?
回答by Langley
There are many ways to do this but I think you are in the right path. Overriding of properties files gets done through BeanFactoryPostProcessors, and there's two implementations that can help you in this case so you don't have to do it from scratch:
有很多方法可以做到这一点,但我认为你走在正确的道路上。覆盖属性文件是通过 BeanFactoryPostProcessors 完成的,在这种情况下,有两个实现可以帮助您,因此您不必从头开始:
PropertySourcesPlaceholderConfigurer & PropertyOverrideConfigurer.
PropertySourcesPlaceholderConfigurer 和 PropertyOverrideConfigurer。
This is an example using PropertySourcesPlaceholderConfigurer:
这是使用 PropertySourcesPlaceholderConfigurer 的示例:
<bean id="someProperties" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" abstract="true" >
<property name="locations">
<list>
<value>classpath:database.properties</value>
<value>classpath:email.properties</value>
</list>
</property>
<property name="ignoreUnresolvablePlaceholders" value="false"/>
</bean>
<bean id="devProperties" parent="someProperties" >
<property name="properties" >
<props >
<prop key="database.username">Database Username used for Development Environment </prop>
</props>
</property>
<property name="localOverride" value="true" />
</bean>
<bean id="testProperties" parent="someProperties" >
<property name="properties" >
<props >
<prop key="database.username">Database Username used for Testing Environment </prop>
</props>
</property>
<property name="localOverride" value="true" />
</bean>
In that example you load the default properties into a bean that will be used as a template for other beans, and in the specific bean, say TestEnvironmentProperties Bean or DevEnvironmentProperties Bean you override only the specific properties you want to override from the default properties files. The example only shows how to override specific properties without the need to create another properties file, from there you can decide how to choose which bean to create with a factory, a simple facade class or a profiles system, anything that you like and matches your architecture.
在该示例中,您将默认属性加载到一个 bean 中,该 bean 将用作其他 bean 的模板,并且在特定 bean 中,例如 TestEnvironmentProperties Bean 或 DevEnvironmentProperties Bean,您仅覆盖您想要从默认属性文件中覆盖的特定属性。该示例仅显示如何覆盖特定属性而无需创建另一个属性文件,从那里您可以决定如何选择使用工厂、简单的外观类或配置文件系统创建哪个 bean,任何您喜欢并匹配您的建筑学。
Also if you think this option is too verbose you can use the property-placeholderelement.
此外,如果您认为此选项过于冗长,您可以使用property-placeholder元素。
I recommend you this book:
我向你推荐这本书:
Getting started with Spring Framework, Second Edition
it has just the examples you need in its 5th chapter. I didn't write it or anything, I just bought it some time ago and I loved it after going through so many bad spring books.
它只有您在第 5 章中需要的示例。我没有写它什么的,我前段时间刚买了它,在阅读了这么多糟糕的春季书籍后我喜欢它。
回答by Alan Hay
Pull the common properties into a separate file and specify that plus the profile specific properties as inputs for each profile. Haven't used the Java based Spring config but here's how I do it in XML. Assume you can do the same in code:
将公共属性提取到单独的文件中,并指定它加上配置文件特定的属性作为每个配置文件的输入。尚未使用基于 Java 的 Spring 配置,但这是我在 XML 中的使用方法。假设你可以在代码中做同样的事情:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<beans profile="default">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/local.profile.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="local">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/local.profile.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="trial">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/trial.profile.properties</value>
</list>
</property>
</bean>
</beans>
<beans profile="live">
<bean id="applicationPropertiesPlaceholder"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:profiles/common.profile.properties</value>
<value>classpath:profiles/live.profile.properties</value>
</list>
</property>
</bean>
</beans>
</beans>
回答by balteo
I think I stumbled upon the beginning of a solution with this interesting blog post.
我想我偶然发现了这篇有趣的博客文章的解决方案的开头。
To quote from the article:
引用文章:
Beware of redundancy of environment-specific properties. For example, if the solution is to have one property file for each environment (e.g. “db-test.properties”, “db-dev.properties”, etc.), then maintaining these properties can be a bit of a nightmare – if a property “foo” is added, then it would have to be added to the property file for each environment (e.g. DEV, TEST, PROD, etc.). The PropertyOverrideConfigurer is appropriate to eliminate this redundancy, setting the default value in the application context itself, but then the overriding value in a separate file. It's important, however, to document this well, since it can look a bit “magical” to an unsuspecting maintenance developer who sees one value specified in the context file, but another used at runtime.
注意环境特定属性的冗余。例如,如果解决方案是为每个环境设置一个属性文件(例如“db-test.properties”、“db-dev.properties”等),那么维护这些属性可能有点像一场噩梦——如果添加了属性“foo”,然后必须将其添加到每个环境(例如 DEV、TEST、PROD 等)的属性文件中。PropertyOverrideConfigurer 适合消除这种冗余,在应用程序上下文本身中设置默认值,然后在单独的文件中设置覆盖值。然而,很好地记录这一点很重要,因为对于毫无戒心的维护开发人员来说,它看起来有点“神奇”,他们看到上下文文件中指定的一个值,但另一个在运行时使用。
The idea is to rely on PropertyOverrideConfigurerand factor out common properties.
这个想法是依靠PropertyOverrideConfigurer并分解出公共属性。
回答by Jim Doyle
The better practice is to put all of the properties files outside of the WAR packaging. You can use a JNDI variable to point Spring to the physical path where the external properties files can be read. Example:
更好的做法是将所有属性文件放在 WAR 包之外。您可以使用 JNDI 变量将 Spring 指向可以读取外部属性文件的物理路径。例子:
<jee:jndi-lookup id="externalFileArea" jndi-name="java:comp/env/mgo/externalFileArea"
default-value="/opt/external/props" lookup-on-startup="true"/>
<util:properties id="myConf" location="file:#{externalFileArea}/my-conf.properties"/>
<!-- And now, to use an entry from this properties import -->
<bean id="foo" class="foo.bar.com">
<property name="configParam1" value="#{myConf['fooConfig.param1']}"
</bean>
If on Windows, the the JNDI entry might be specified as /C/Users/someone. Finally, add a file named /opt/external/props/my-conf.properties, and in there place an entry like: fooConfig.param1=true
如果在 Windows 上,JNDI 条目可能指定为 /C/Users/someone。最后,添加一个名为 /opt/external/props/my-conf.properties 的文件,并在其中放置一个条目,如:fooConfig.param1=true
Wash, rinse, repeat. Far less work, much more secure and much more easy to maintain.
清洗,冲洗,重复。更少的工作,更安全,更容易维护。
回答by Michael Andrews
I would suggest that "common" properties do not need to be in a common file, and instead can be default values of property placeholder in-line in your code. This allows them to be overridden via JVM args (or local env) while not needing to be "managed" in a file. Your environment-specific properties, in your environment-specific files, then indicate just those properties that MUST be provided in each environment for the app to start up. As such they would NOT have default values in placeholders.
我建议“公共”属性不需要在公共文件中,而是可以是代码中内联属性占位符的默认值。这允许它们通过 JVM 参数(或本地环境)被覆盖,而无需在文件中“管理”。您的环境特定属性,在您的环境特定文件中,然后仅指示必须在每个环境中提供的那些属性,以便应用程序启动。因此,它们在占位符中不会有默认值。