如何在Spring XML上下文中实现条件资源导入?

时间:2010-06-14 07:37:18

标签: java xml spring

我想要实现的是“动态”(即基于配置文件中定义的属性)启用/禁用子Spring XML上下文的导入。

我想象的是:

<import condition="some.property.name" resource="some-context.xml"/>

属性被解析(到布尔值),当为真时,导入上下文,否则不是。

到目前为止我的一些研究:

  • 编写自定义NamespaceHandler(及相关类),以便在自己的命名空间中注册自己的自定义元素。例如:<myns:import condition="some.property.name" resource="some-context.xml"/>

    这种方法的问题在于我不想从Spring复制整个资源导入逻辑,对我来说,我需要委派什么来做这件事并不明显。

  • 覆盖DefaultBeanDefinitionDocumentReader以扩展“import”元素解析和解释的行为(在importBeanDefinitionResource方法中发生)。但是我不知道我可以在哪里注册这个扩展名。

8 个答案:

答案 0 :(得分:40)

在Spring 4之前,使用标准Spring组件的最接近的是:

<import resource="Whatever-${yyzzy}.xml"/>

其中${xyzzy}从系统属性中插入属性。 (我使用了一个hacky自定义版本的上下文加载器类,它在开始加载过程之前将其他地方的属性添加到系统属性对象。)

但是你也可以通过导入大量不必要的东西来逃脱......并使用各种技巧只能导致必要的bean被实例化。这些技巧包括:

  • 占位符和财产替换
  • 使用新的Spring表达式语言
  • 选择不同的bean
  • 目标名称中包含占位符的bean别名
  • 懒惰的bean初始化,
  • 智能豆工厂。

答案 1 :(得分:23)

使用Spring 3.1.x,您可以使用bean profiles来实现条件资源导入和bean实例化。如果您使用的是早期版本,这当然没有任何帮助:)

答案 2 :(得分:23)

如前所述,如果您使用的是Spring 3.1 +

,则可以使用配置文件轻松完成
<!-- default configuration - will be loaded if no profile is specified -->
<!-- This will only work if it's put at the end of the configuration file -->
<!-- so no bean definitions after that -->
<beans profile="default">
    <import resource="classpath:default.xml" />
</beans>
<!-- some other profile -->
<beans profile="otherProfile">
    <import resource="classpath:other-profile.xml" />
</beans>

otherProfile可以通过例如

轻松激活
mvn install -Dspring.profiles.active=otherProfile

如果你在测试中使用不同的配置文件,只需添加-DforkMode=never以确保测试将在同一个VM内运行,因此param spring.profiles.active不会丢失

答案 3 :(得分:23)

现在完全可以使用Spring 4。

在主应用程序内容文件

<bean class="com.example.MyConditionalConfiguration"/>

MyConditionalConfiguration看起来像

@Configuration
@Conditional(MyConditionalConfiguration.Condition.class)
@ImportResource("/com/example/context-fragment.xml")
public class MyConditionalConfiguration {
    static class Condition implements ConfigurationCondition {
         @Override
         public ConfigurationPhase getConfigurationPhase() {
             return ConfigurationPhase.PARSE_CONFIGURATION;
         }
         @Override
         public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
             // only load context-fragment.xml if the system property is defined
             return System.getProperty("com.example.context-fragment") != null;
         }
    }
}

然后最后,将所需的bean定义包含在/com/example/context-fragment.xml

请参阅JavaDoc for @Conditional

答案 4 :(得分:20)

为了记录,Robert Maldon解释了如何在这篇文章中完成bean的条件定义:http://robertmaldon.blogspot.com/2007/04/conditionally-defining-spring-beans.html。在这里复制它有点长(此外,我认为我不应该复制粘贴他的文章)。

根据您的示例,这种方法的最终结果是:

<condbean:cond test="${some.property.name}">
  <import resource="some-context.xml"/>
</condbean:cond>

这肯定不像斯蒂芬C的解决方案那么简单,但它更强大。

答案 5 :(得分:0)

另一种选择是让您的应用加载位于/ conf文件夹中的modules-config.xml文件,并在安装/配置阶段对其进行编辑,以取消注释要加载的模块。

这是我用于Web应用程序的解决方案,该应用程序充当不同集成模块的容器。 Web应用程序随所有不同的集成模块一起分发。 modules-config.xml放在tomcat的/ conf文件夹中,conf文件夹添加到类路径中(通过catalina.properties/common.loader属性)。我的网络应用程序webapp-config.xml有一个<import resource="classpath:/modules-config.xml"/>来加载它。

答案 6 :(得分:0)

另一个需要考虑的是Spring 3.0:

 <alias name="Whatever" alias=""Whatever-${yyzzy}" />

其中${xyzzy}从系统属性中插入属性。

答案 7 :(得分:0)

您可以在自己的ContextLoaderListener中重写contextInitialized(javax.servlet.ServletContextEvent事件),并在调用super.contextInitialized(event)之前设置所需的System属性

package com.mypackage;
import org.springframework.web.context.ContextLoaderListener;
public class MyContextLoaderListener extends ContextLoaderListener {
    public void contextInitialized(javax.servlet.ServletContextEvent event) {
        System.setProperty("xyz", "import-file-name.xml");
        super.contextInitialized(event);
    }
}

而不是在您的web.xml中将ContextLoaderListener替换为MyContextLoaderListener

<listener>
    <listener-class>com.mypackage.MyContextLoaderListener</listener-class>
</listener>

现在您可以在spring.xml中使用

<import resource="${xyz}" /> 

我希望这会有所帮助。