【问题标题】:Correctly using Spring environment profiles in order to manage PropertySourcesPlaceholderConfigurer and sets of properties files正确使用 Spring 环境配置文件以管理 PropertySourcesPlaceholderConfigurer 和属性文件集
【发布时间】:2014-06-30 09:00:50
【问题描述】:

我正在开发一个 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 个不同的文件夹中重复,因为只有少数值不同。

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

有人可以帮忙吗?

【问题讨论】:

    标签: spring environment-variables properties-file spring-profiles


    【解决方案1】:

    将公共属性放入一个单独的文件中,并指定该文件以及配置文件特定属性作为每个配置文件的输入。没有使用基于 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>
    

    【讨论】:

    • 谢谢艾伦!嗯,唯一的问题是您的解决方案迫使一个人在同一文件中具有不相关的属性(即 common.profile.properties)...
    • 建议的解决方案的另一个问题是,如果由于某种原因在某些时候我需要为其中一个属性设置不同的值,我需要将其从公用文件中拉出并将其放置在特定于配置文件中文件。
    【解决方案2】:

    我想我偶然发现了这个 interesting blog post 的解决方案。

    引用文章:

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

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

    【讨论】:

      【解决方案3】:

      有很多方法可以做到这一点,但我认为你走在正确的道路上。 属性文件的覆盖是通过 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 元素。

      我向你推荐这本书:

      Getting started with Spring Framework, Second Edition

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            猜你喜欢
            • 2015-11-22
            • 2015-02-18
            • 2012-11-25
            • 1970-01-01
            • 2013-07-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多