Java 根据 .properties 文件中的属性导入 Spring 配置文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1520055/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 13:46:12  来源:igfitidea点击:

Import Spring config file based on property in .properties file

javaspringconfiguration

提问by nash2000

In my Spring xml configuration I'm trying to get something like this to work:

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

<beans>

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

   <!-- Other bean definitions -->

</beans>

I want to decide which file to import based on a property in a properties file. I know that I can use a System property, but I can't add a property to the JVM at startup.

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

Note: The PropertyPlaceHolderConfigurer will notwork. Imports are resolved before any BeanFactoryPostProcessors are run. The import element can only resolve System.properties.

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

Does anyone have a simple solution to this? I don't want to start subclassing framework classes and so on...

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

Thanks

谢谢

回答by Brian Agnew

Why not:

为什么不:

  1. read your properties file on startup
  2. that will determine which Spring config to load
  3. whichever Spring config is loaded sets specific stuff, thenloads a common Spring config
  1. 在启动时读取您的属性文件
  2. 这将决定要加载哪个 Spring 配置
  3. 无论加载哪个 Spring 配置,都会设置特定的内容,然后加载一个通用的 Spring 配置

so you're effectively inverting your current proposed solution.

所以你有效地颠倒了你当前提出的解决方案。

回答by laz

Add something similar to the following:

添加类似于以下内容的内容:

<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>

回答by Louis Marascio

This is, unfortunately, a lot harder than it should be. In my application I accomplished this by doing the following:

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

  1. A small, "bootstrap" context that is responsible for loading a PropertyPlaceholderConfigurer bean and another bean that is responsible for bootstrapping the application context.

  2. The 2nd bean mentioned above takes as input the "real" spring context files to load. I have my spring context files organized so that the configurable part is well known and in the same place. For example, I might have 3 config files: one.onpremise.xml, one.hosted.xml, one.multitenant.xml. The bean programmatically loads these context files into the current application context.

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

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

This works because the context files are specified as input the the bean responsible for loading them. It won't work if you just try to do an import, as you mentioned, but this has the same effect with slightly more work. The bootstrap class looks something like this:

这是有效的,因为上下文文件被指定为负责加载它们的 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();
    }
}

Basically, get the application context, set its config locations, and tell it to refresh itself. This works perfectly in my application.

基本上,获取应用程序上下文,设置其配置位置,并告诉它自己刷新。这在我的应用程序中非常有效。

Hope this helps.

希望这可以帮助。

回答by Jaan

If what you want is to specify the imported XML file name outside applicationContext.xml so that you could replace applicationContext.xml without losing the configuration of the imported XML file path, you can just add an intermediate Spring beans XML file, say, confSelector.xml, so that applicationContext.xml imports confSelector.xml and confSelector.xml only contains an <import> element that refers to the suitable custom beans XML file.

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

Another means that might be of use are XML entities (defined by adding <!ENTITY ... > elements into the DTD declaration at the beginning of XML). These allow importing XML fragments from other files and provide "property placeholder"-like functionality for any XML file.

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

Neither of these solutions allows you to have the configuration file in Java's .properties format, though.

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

回答by Ian Brandt

There is an old issue on the Spring JIRA for adding properties placeholder support for import (SPR-1358)that was resolved as "Won't Fix", but there has since been a proposed solution using an EagerPropertyPlaceholderConfigurer.

Spring JIRA 上存在一个旧问题,用于添加属性占位符支持导入 (SPR-1358),该问题已解决为“无法修复”,但此后提出了使用 EagerPropertyPlaceholderConfigurer 的解决方案。

I've been lobbying to have SPR-1358 reopened, but no response so far. Perhaps if others added their use cases to the issue comments that would help raise awareness.

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

回答by André Schuster

I'm using Spring 3 and load a properties like that:

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

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

回答by István

For the Spring 2.5 and 3.0, I have a similar solutionto louis, however I've just read about 3.1's upcoming feature: property management, which sounds great too.

对于 Spring 2.5 和 3.0,我有一个与louis类似的解决方案,但是我刚刚阅读了 3.1 即将推出的功能:property management,这听起来也很棒。

回答by Russ Bateman

André Schuster's answer, which I bumped, helped me solve a very similar issue I was having in wanting to find a different expression of properties depending on whether I was running on my own host, by Jenkins on our build host or in "real" deployment. I did this:

我遇到的 André Schuster 的回答帮助我解决了一个非常相似的问题,我希望找到不同的属性表达方式,这取决于我是在自己的主机上运行,​​由 Jenkins 在我们的构建主机上运行还是在“真实”部署中. 我这样做了:

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

followed later by

随后是

<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>

which solved my problem because on my development host, I put a link to my own copy of database.propertiesin /etc/myplace/database.properties, and a slightly different one on the server running Jenkins. In real deployment, no such file is found, so Spring falls back on the "real" one in resourcesin my class files subdirectory. If the properties in question have already been specified by the file on /etc/myplace/database.properties, then (fortunately) they aren't redefined by the local file.

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

回答by Antoine Meyer

Another workaround which does not rely on system properties is to load the properties of all the files using a different PropertyPlaceholderConfigurer for each file and define a different placeholderPrefix for each of them. That placeholderprefix being configured by the initial property file.

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




Define the first property file: (containing either first or second)


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

global.properties

global.properties

fileToUse=first


Define the files containing a property that can be switched depending on the property defined just above:


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

first.properties

first.properties

aProperty=propertyContentOfFirst

second.properties

第二个属性

aProperty=propertyContentOfSecond


Then define the place holders for all the files:


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

<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>


Use the property defined in global to identify the resource to use from the other file:


使用 global 中定义的属性从另一个文件中标识要使用的资源:

${fileToUse}{aProperty}

回答by gabbon

If I add the JVM argument below and have the file myApplicationContext.dev.xml, spring does load

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

-DmyEnvironment=dev

-DmyEnvironment=dev

<context:property-placeholder />

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