【问题标题】:Import Spring config file based on property in .properties file根据 .properties 文件中的属性导入 Spring 配置文件
【发布时间】:2010-12-03 22:51:28
【问题描述】:

在我的 Spring xml 配置中,我试图让这样的事情起作用:

<beans>

   <import resource="${file.to.import}" />

   <!-- Other bean definitions -->

</beans>

我想根据属性文件中的属性来决定要导入哪个文件。 我知道我可以使用系统属性,但我无法在启动时向 JVM 添加属性。

注意:PropertyPlaceHolderConfigurer 将不工作。在运行任何 BeanFactoryPostProcessors 之前解析导入。 import 元素只能解析 System.properties。

有人对此有简单的解决方案吗?我不想开始子类化框架类等等......

谢谢

【问题讨论】:

  • 你解决过这个问题吗?我有完全相同的问题...
  • 我有相反的问题。导入资源有效,但 value = ${something} 无效
  • 您也可以使用注释来做到这一点@PropertySource("classpath:my-file.properties")

标签: java spring configuration


【解决方案1】:

不幸的是,这比它应该做的要困难得多。在我的应用程序中,我通过执行以下操作来完成此操作:

  1. 一个小型的“引导”上下文,负责加载 PropertyPlaceholderConfigurer bean 和另一个负责引导应用程序上下文的 bean。

  2. 上面提到的第二个 bean 将要加载的“真实”spring 上下文文件作为输入。我组织了我的 spring 上下文文件,以便可配置部分是众所周知的并且在同一个地方。例如,我可能有 3 个配置文件:one.onpremise.xml、one.hosted.xml、one.multitenant.xml。 bean 以编程方式将这些上下文文件加载到当前应用程序上下文中。

这是可行的,因为上下文文件被指定为负责加载它们的 bean 的输入。正如您所提到的,如果您只是尝试进行导入,它将无法正常工作,但这具有相同的效果,只需稍加工作即可。引导类看起来像这样:

 public class Bootstrapper implements ApplicationContextAware, InitializingBean {

    private WebApplicationContext context;
    private String[] configLocations;
    private String[] testConfigLocations;
    private boolean loadTestConfigurations;

    public void setConfigLocations(final String[] configLocations) {
        this.configLocations = configLocations;
    }

    public void setTestConfigLocations(final String[] testConfigLocations) {
        this.testConfigLocations = testConfigLocations;
    }

    public void setLoadTestConfigurations(final boolean loadTestConfigurations) {
        this.loadTestConfigurations = loadTestConfigurations;
    }

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
        context = (WebApplicationContext) applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        String[] configsToLoad = configLocations;

        if (loadTestConfigurations) {
            configsToLoad = new String[configLocations.length + testConfigLocations.length];
            arraycopy(configLocations, 0, configsToLoad, 0, configLocations.length);
            arraycopy(testConfigLocations, 0, configsToLoad, configLocations.length, testConfigLocations.length);
        }

        context.setConfigLocations(configsToLoad);
        context.refresh();
    }
}

基本上,获取应用程序上下文,设置其配置位置,并告诉它自己刷新。这在我的应用程序中完美运行。

希望这会有所帮助。

【讨论】:

  • 注意:这段代码取自一个工作系统,但我手动清理了一些与系统本身相关的小片段。如有错误请见谅。
  • 谢谢,但不幸的是,我无法完全控制启动,因为 EJB 是应用程序的入口点。也许我应该升级到 Spring 3 并改用 JavaConfig :)
  • 我认为除了能够告诉 EJB 要加载哪个弹簧上下文之外,您不需要任何控制。我错过了什么吗?
  • 我很惊讶这个解决方案没有被接受。它简单而精彩
  • 我同意肖恩的观点。 @Louis 确实很聪明。
【解决方案2】:

对于 Spring 2.5 和 3.0,I have a similar solution 给 louis,不过我刚刚阅读了 3.1 即将推出的功能:property management,这听起来也很棒。

【讨论】:

  • +1 用于链接到 Spring 3.1 属性管理 - 不错的新功能!
  • 有人有这样的例子吗?无法使其与 3.1.1 一起使用。
【解决方案3】:

在 Spring JIRA 上存在一个添加 properties placeholder support for import (SPR-1358) 的旧问题,该问题已解决为“无法修复”,但此后提出了使用 EagerPropertyPlaceholderConfigurer 的解决方案。

我一直在游说重新开放 SPR-1358,但到目前为止没有任何回应。也许如果其他人将他们的用例添加到问题 cmets 中,这将有助于提高认识。

【讨论】:

    【解决方案4】:

    为什么不:

    1. 在启动时读取您的属性文件
    2. 这将决定加载哪个 Spring 配置
    3. 无论加载哪个 Spring 配置设置特定的东西,然后加载一个通用 Spring 配置

    因此,您实际上是在颠倒当前提出的解决方案。

    【讨论】:

    • 应用程序的入口点是一个 EJB,它在第一个方法调用时加载 ApplicationContext,所以我真的不能按照你的建议去做。
    【解决方案5】:

    添加类似以下内容:

    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="ignoreResourceNotFound"><value>true</value></property>
        <property name="locations">
            <list>
                <value>classpath:propertyfile.properties</value>
            </list>
        </property>
    </bean>
    

    【讨论】:

    • 谢谢,但是 PropertyPlaceHolderConfigurer 将不起作用:)
    • 无赖。我看到你添加的关于后处理器和导入顺序的注释。
    【解决方案6】:

    如果您想要在 applicationContext.xml 之外指定导入的 XML 文件名,这样您就可以替换 applicationContext.xml 而不会丢失导入的 XML 文件路径的配置,您可以添加一个中间 Spring beans XML 文件,比如, confSelector.xml,以便 applicationContext.xml 导入 confSelector.xml 并且 confSelector.xml 仅包含一个 元素,该元素引用了合适的自定义 beans XML 文件。

    另一种可能有用的方法是 XML 实体(通过在 XML 开头的 DTD 声明中添加 元素来定义)。这些允许从其他文件导入 XML 片段,并为任何 XML 文件提供类似“属性占位符”的功能。

    不过,这些解决方案都不允许您拥有 Java 的 .properties 格式的配置文件。

    【讨论】:

      【解决方案7】:

      我遇到的 André Schuster 的回答帮助我解决了一个非常相似的问题,我想根据我是在自己的主机上、Jenkins 在我们的构建主机上运行还是在“真正的”部署。我这样做了:

      <context:property-placeholder location="file:///etc/myplace/database.properties" />
      

      后来

      <bean id="propertyConfigurer"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
          <property name="locations">
              <list>
                  <value>WEB-INF/classes/resources/database.properties</value>
                  ...
              </list>
          </property>
      </bean>
      

      这解决了我的问题,因为在我的开发主机上,我在 /etc/myplace/database.properties 中放置了指向我自己的 database.properties 副本的链接,并且在运行 Jenkins 的服务器上略有不同。在实际部署中,找不到这样的文件,因此 Spring 在我的类文件子目录中的 resources 中使用“真正的”文件。如果有问题的属性已经由 /etc/myplace/database.properties 上的文件指定,那么(幸运的是)它们不会被本地文件重新定义。

      【讨论】:

        【解决方案8】:

        另一个不依赖于系统属性的解决方法是为每个文件使用不同的 PropertyPlaceholderConfigurer 加载所有文件的属性,并为每个文件定义不同的 placeholderPrefix。 该占位符前缀由初始属性文件配置。



        定义第一个属性文件:(包含第一个或第二个)

        global.properties

        fileToUse=first
        


        定义包含可以根据上面定义的属性进行切换的属性的文件:

        first.properties

        aProperty=propertyContentOfFirst
        

        second.properties

        aProperty=propertyContentOfSecond
        


        然后为所有文件定义占位符:

        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:global.properties</value>
                </list>
            </property>
        </bean>
        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="placeholderPrefix" value="first{" />
            <property name="locations">
                <list>
                    <value>classpath:first.properties</value>
                </list>
            </property>
        </bean>
        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="placeholderPrefix" value="second{" />
            <property name="locations">
                <list>
                    <value>classpath:second.properties</value>
                </list>
            </property>
        </bean>
        


        使用 global 中定义的属性从其他文件中识别要使用的资源:

        ${fileToUse}{aProperty}
        

        【讨论】:

          【解决方案9】:

          如果我在下面添加 JVM 参数并拥有 myApplicationContext.dev.xml 文件,spring 会加载

          -DmyEnvironment=dev

          <context:property-placeholder />
          
          <import resource="classpath:/resources/spring/myApplicationContext.${myEnvironment}.xml"/>
          

          【讨论】:

            【解决方案10】:

            我正在使用 Spring 3 并加载这样的属性:

            <context:property-placeholder location="/WEB-INF/my.properties" />
            

            【讨论】:

            • 但这与问题无关。 &lt;import&gt; 标记不适用于属性占位符。
            • 我同意肖恩的观点。属性占位符是一个 beanfactory 后处理器。
            猜你喜欢
            • 2021-10-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-05-27
            • 2018-07-15
            • 1970-01-01
            • 2013-03-14
            • 1970-01-01
            相关资源
            最近更新 更多