2010-06-14 34 views
52

我想实现的是“动态”(即基于配置文件中定义的属性)启用/禁用导入子级Spring XML上下文的功能。如何在Spring XML上下文中实现有条件的资源导入?

我想是这样的:

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

凡该财产被解析(一个布尔值),当真实的背景是进口的,否则就不是。

我的一些研究,迄今:

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

    这种方法的问题是我不想从Spring复制整个资源导入逻辑,并且我不明白我需要委托来执行此操作。

  • 覆盖DefaultBeanDefinitionDocumentReader以扩展“导入”元素解析和解释(这发生在importBeanDefinitionResource方法中)的行为。不过,我不确定我可以在哪里注册此扩展程序。
+0

而不是做条件导入,为什么不使用类路径扫描,只部署所需的配置?我发现条件导入更复杂,并且在查看已部署的应用程序时更难确定什么是/未配置的。 – SteveD 2010-06-14 08:20:26

+0

如何定义“所需配置”?我们有部分功能很好地模块化,并在上下文加载时自动激活(白板模式)。但是我们需要一种机制来动态(读取:在安装/配置时)激活和停用这些功能。它是一种轻量级的插件系统。 – 2010-06-14 08:34:08

回答

37

你可以使用标准的Spring组件最接近的是:

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

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

但是,您也可以逃避导入大量不必要的东西......并且使用各种技巧只会导致必要的bean被实例化。这些技巧包括:

  • 占位符和财产替代
  • 使用新的Spring表达式语言选择不同的豆子,
  • 豆别名与目标名称的占位符,
  • 懒豆初始化,
  • 智能豆工厂。
+0

我不想实际触及相关模块的上下文,并以这种方式阻止bean实例化。我真的想保留模块的上下文边界并在该级别上切换。 我曾经考虑过资源URI的属性替代品,但正如你所说的那样,只考虑系统属性,它只允许我切换到不同的上下文,而不是禁用它。 也许我将不得不提供一个通用的空“禁用模块context.xml”? – 2010-06-14 12:04:45

+0

@Boris - 这就是我所做的。查看SourceForge上的“metadata-net”项目,并查看Danno/Emmet/Chico配置如何工作。它仍然是一个正在进行的工作,但你可能会借用一些想法。 – 2010-06-14 12:17:42

+6

这只适用于系统属性。导入发生时,上下文占位符的属性尚未加载。 (http://stackoverflow.com/questions/5253546/are-properties-read-by-a-spring-property-placeholder-immediately-available) – 2012-07-03 14:06:55

21

罗伯特·马尔东在这篇文章中解释了如何完成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'S解决方案很简单,但它更飞机的强大动力。

22

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

21

如前所述,这可以很容易地完成配置文件,如果你使用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内运行,因此帕拉姆spring.profiles.active不会丢失

+0

配置文件很好,但是如何调用Condition的其他实现呢?那可能吗? – 2016-09-08 07:04:42

0

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

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

0

另外一个要考虑春季3.0:

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

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

18

现在这是完全有可能的,使用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; 
     } 
    } 
} 

然后终于,你把要包含在/com/example/context-fragment.xml中的bean定义

查看JavaDoc for @Conditional

+0

非常酷!感谢所有这一次更新。很高兴他们终于为此做了一些事情。 – 2014-08-14 08:11:04

+0

我们在这里也找不到占位符。只能使用System.property。 – 2014-12-20 05:53:55

+0

最初的问题是关于仅限条件导入,而不是使用占位符。所以这实际上是正确的答案。 – Jakub 2016-01-21 10:45:25

相关问题