正确使用Spring环境配置文件来管理PropertySourcesPlaceholderConfigurer和属性文件集合。

15

我正在开发一个Spring应用程序,意识到我的属性管理方式存在问题。我使用Spring环境配置文件来加载属性,最近添加了更多配置文件使得我的属性文件变得难以管理。

这些属性文件位于src/main/resources/META-INF/props/下的不同文件夹中,每个文件夹对应不同的Spring环境配置文件。

现在我至少有5个配置文件,这意味着我有5个子文件夹,每个子文件夹包含相同名称但仅对某些键具有不同值的属性文件。

这是它的样子:

properties file screen capture

这是我如何配置我的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;
        }
     ...
    }

总之,我的问题如下:

  • 由于只有少量值不同,所以在5个不同的文件夹中到处都是键/值对的重复。

因此,我正在寻找一种新的策略来管理不同环境之间的差异。

有人能帮忙吗?

5个回答

5

有许多方法可以实现此目的,但我认为您正在正确的道路上。属性文件的覆盖是通过BeanFactoryPostProcessors完成的,在这种情况下,有两个实现可以帮助您,因此您不必从头开始:

PropertySourcesPlaceholderConfigurer和PropertyOverrideConfigurer。

以下是使用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>

在这个例子中,您将默认属性加载到一个bean中,该bean将用作其他bean的模板,在特定的bean(例如TestEnvironmentProperties Bean或DevEnvironmentProperties Bean)中,您只覆盖要从默认属性文件中覆盖的特定属性。此示例仅显示如何覆盖特定属性,而无需创建另一个属性文件,从那里,您可以决定如何选择使用工厂、简单的外观类或配置文件系统创建哪个bean,以及任何符合您体系结构的东西。

如果您认为此选项过于冗长,您可以使用property-placeholder元素。

我向您推荐这本书:

Getting started with Spring Framework,Second Edition

第5章正好有您需要的例子。我并没有写这本书或做任何事情,只是一段时间前购买了它,并在浏览了很多糟糕的Spring书籍后爱上了它。


2
将常用属性提取到一个单独的文件中,并将其与特定于配置文件的属性一起指定为每个配置文件的输入。虽然我没有使用基于Java的Spring config,但是以下是我在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>

谢谢Alan!唔,唯一的问题是你的解决方案强制要求在同一个文件中拥有不相关的属性(即common.profile.properties)... - balteo
1
建议解决方案的另一个问题是,如果在某个时刻由于某种原因我需要不同的属性值,我需要从公共文件中提取它,并将其放置在特定于配置文件的文件中。 - balteo

0
我建议"常见"属性不必在一个公共文件中,而应该作为代码中属性占位符的默认值嵌入其中。这样可以通过JVM参数(或本地环境)进行覆盖,而无需在文件中进行"管理"。您特定于环境的属性,可以在您特定于环境的文件中设置,然后仅指示每个环境必须提供的那些属性,以便应用程序启动。因此,它们在占位符中不会有默认值。

0

我认为我在这篇有趣的博客文章中找到了解决方案的开端。

引用文章中的一段话:

注意环境特定属性的冗余。例如,如果解决方案是为每个环境(如“db-test.properties”,“db-dev.properties”等)拥有一个属性文件,则维护这些属性可能会有点困难 - 如果添加了一个属性“foo”,则必须将其添加到每个环境的属性文件中(例如DEV,TEST,PROD等)。 PropertyOverrideConfigurer适用于消除此冗余,在应用程序上下文本身中设置默认值,但在单独的文件中覆盖该值。然而,重要的是要对此进行充分的记录,因为对于看到在上下文文件中指定了一个值,但在运行时使用另一个值的维护开发人员来说,这可能看起来有点“神奇”。

这个想法是依赖于PropertyOverrideConfigurer并提取出共同的属性。


0
更好的做法是将所有属性文件放在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>

如果在Windows上,JNDI条目可能被指定为/C/Users/someone。 最后,在其中放置一个名为/opt/external/props/my-conf.properties的文件,并在其中放置一个条目,如下所示:fooConfig.param1=true

洗涤,漂洗,重复。工作量更少,更安全,更易于维护。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接