15

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

属性文件位于 内的不同文件夹中src/main/resources/META-INF/props/,每个文件夹与不同的 Spring 环境配置文件匹配。

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

这是它的外观:

属性文件屏幕截图

这是我配置我的 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 个不同的文件夹中重复,因为只有几个值不同。

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

有人可以帮忙吗?

4

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 中,例如 TestEnvironmentProperties Bean 或 DevEnvironmentProperties Bean,您仅覆盖您想要从默认属性文件中覆盖的特定属性。该示例仅显示如何覆盖特定属性而无需创建另一个属性文件,从那里您可以决定如何选择使用工厂、简单外观类或配置文件系统创建哪个 bean,任何您喜欢并匹配您的建筑学。

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

我向你推荐这本书:

Spring Framework 入门,第二版

它在第 5 章中提供了您需要的示例。我没有写它什么的,我前段时间才买的,看了这么多糟糕的春书后我很喜欢它。

于 2014-08-15T17:46:43.327 回答
2

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:

<?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>
于 2014-05-12T20:58:37.057 回答
0

我想我是通过这篇有趣的博客文章偶然发现了一个解决方案的开始。

引用文章:

注意环境特定属性的冗余。例如,如果解决方案是为每个环境拥有一个属性文件(例如“db-test.properties”、“db-dev.properties”等),那么维护这些属性可能有点像噩梦——如果添加一个属性“foo”,然后必须将它添加到每个环境(例如 DEV、TEST、PROD 等)的属性文件中。PropertyOverrideConfigurer 适合消除这种冗余,在应用程序上下文本身中设置默认值,然后在单独的文件中设置覆盖值。然而,很好地记录这一点很重要,因为对于毫无戒心的维护开发人员来说,这可能看起来有点“神奇”,他们看到上下文文件中指定了一个值,但在运行时使用了另一个值。

这个想法是依靠PropertyOverrideConfigurer并分解出公共属性。

于 2014-05-13T15:48:33.100 回答
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

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

于 2015-07-04T23:19:01.033 回答
0

我建议“通用”属性不需要位于通用文件中,而可以是代码中的属性占位符的默认值。这允许它们通过 JVM 参数(或本地环境)被覆盖,而无需在文件中“管理”。然后,在特定于环境的文件中,您的特定于环境的属性仅指示必须在每个环境中提供以使应用程序启动的那些属性。因此,它们不会在占位符中具有默认值。

于 2016-03-23T12:14:29.990 回答