2010-06-14 9 views
52

Lo que me gustaría lograr es la capacidad de "dinámicamente" (es decir, basada en una propiedad definida en un archivo de configuración) activar/desactivar la importación de un contexto Spring XML infantil.¿Cómo se logra la importación de recursos condicional en un contexto XML de Spring?

me imagino algo como:

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

Cuando se resuelve la propiedad (a un valor lógico) y cuando el contexto cierto es importado, de lo contrario, no lo es.

Algunos de mis investigaciones hasta el momento:

  • Escribir una costumbre NamespaceHandler (y clases relacionadas) así que puedo registrar mi propio elemento personalizado en mi propio espacio de nombres. Por ejemplo: <myns:import condition="some.property.name" resource="some-context.xml"/>

    El problema con este enfoque es que no quiero replicar toda la lógica de importación de recursos de Spring y no es obvio para qué debo delegar para hacerlo.

  • Anulando DefaultBeanDefinitionDocumentReader para extender el comportamiento del análisis e interpretación del elemento "importación" (que ocurre allí en el método importBeanDefinitionResource). Sin embargo, no estoy seguro de dónde puedo registrar esta extensión.
+0

En lugar de realizar una importación condicional, ¿por qué no utilizar el escaneo de ruta de clase y desplegar solo la configuración requerida? Encuentro que la importación condicional es más compleja y es más difícil averiguar qué es/no está configurado cuando se mira una aplicación desplegada. – SteveD

+0

¿Cómo se define la "configuración requerida"? Tenemos partes de la funcionalidad que están muy bien modularizadas y se activan automáticamente cuando se carga el contexto (patrón de la pizarra). Pero necesitamos un mecanismo para dinámicamente (leer: en el momento de la instalación/configuración) activar y desactivar estas piezas de funcionalidad. Es un tipo de sistema de complemento ligero. –

Respuesta

37

Lo más cerca que se puede obtener utilizando componentes de muelle estándar es:

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

donde ${xyzzy} interpola una propiedad de las propiedades del sistema. (Utilizo una versión personalizada de la clase de cargador de contexto que agrega propiedades de otros lugares al objeto de propiedades del sistema antes de iniciar el proceso de carga).

Pero también puede salirse con la suya importando muchas cosas innecesarias ... y utilice varios trucos para causar que los beans necesarios sean instanciados. Estos trucos incluyen:

  • marcador de posición y la sustitución de la propiedad
  • la selección de diferentes granos utilizando el nuevo lenguaje de expresión de primavera,
  • alias de frijol con marcadores de posición en el nombre de destino,
  • inicialización frijol perezoso, y
  • fábricas de judías inteligentes.
+0

No quiero tocar realmente el contexto del módulo en cuestión y evitar la creación de instancias de beans de esa manera. Realmente me gustaría mantener el límite de contexto para el módulo y alternar en ese nivel. Había considerado el sustituto de propiedad para el URI de recursos pero, como dijiste, solo se consideran las propiedades del sistema y solo me permite cambiar a un contexto diferente, no para deshabilitar uno. Tal vez debería proporcionar un "módulo-contexto-XML.xml deshabilitado" genérico y vacío? –

+0

@ Boris - eso es lo que hago. Eche un vistazo al proyecto "metadata-net" en SourceForge, y vea cómo funcionan las configuraciones de Danno/Emmet/Chico. Todavía es algo así como un trabajo en progreso, pero probablemente puedas tomar algunas ideas. –

+6

Esto solo funciona para las propiedades del sistema. Las propiedades del contenedor de posición de contexto aún no se cargan cuando se realiza la importación. (http://stackoverflow.com/questions/5253546/are-properties-read-by-a-spring-property-placeholder-immediately-available) –

21

Para el registro, Robert Maldon explica cómo lograr la definición condicional de frijoles en esta publicación: http://robertmaldon.blogspot.com/2007/04/conditionally-defining-spring-beans.html. Es un poco largo copiarlo aquí (además, no creo que deba copiar y pegar su artículo de todos modos).

El resultado final de este enfoque, adaptado para su ejemplo, es:

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

Ciertamente, no es tan simple como una solución de Stephen C, pero es mucho más poweful.

22

Con Spring 3.1.x puede usar bean profiles para lograr la importación de recursos condicional y la creación de instancias de beans.Esto es, por supuesto, de ninguna ayuda si está utilizando una versión anterior :)

21

Como se mencionó anteriormente, esto se puede lograr fácilmente con perfiles si está utilizando 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 puede ser fácilmente activado con, por ejemplo,

mvn install -Dspring.profiles.active=otherProfile 

Si estás utilizando diferentes perfiles en las pruebas, sólo tiene que añadir -DforkMode=never para asegurarse de que las pruebas se ejecutarán en el interior mismo de VM, por lo tanto, el parámetro spring.profiles.active no será perdido

+0

Los perfiles son geniales, pero ¿qué pasa si invocamos alguna otra implementación de Condition? ¿Es eso posible? –

0

Otra opción es tener su aplicación cargue un archivo modules-config.xml que esté ubicado en la carpeta/conf y edítelo durante la fase de instalación/configuración para descomentar los módulos que desea cargar.

Esta es la solución que estoy usando con una aplicación web que sirve como contenedor para diferentes módulos de integración. La aplicación web se distribuye con todos los diferentes módulos de integración. Un modules-config.xml se coloca en la carpeta tomcat/conf y la carpeta conf se agrega al classpath (a través de la propiedad catalina.properties/common.loader). Mi aplicación web webapp-config.xml tiene un <import resource="classpath:/modules-config.xml"/> para que se cargue.

0

Otro a tener en cuenta para la primavera 3,0:

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

donde ${xyzzy} interpola una propiedad de las propiedades del sistema.

18

Esto es ahora totalmente posible, utilizando Spring 4.

En el archivo de contenido de la aplicación principal

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

Y el MyConditionalConfiguration parece

@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; 
     } 
    } 
} 

Y, por último, se pone el definiciones de bean que desee incluir en /com/example/context-fragment.xml

Ver el JavaDoc for @Conditional

+0

¡Muy bueno! Gracias por actualizar después de todo este tiempo. Me alegro de que finalmente hicieron algo por esto. –

+0

No podemos obtener el marcador de posición aquí también. Solo se puede usar System.property. –

+0

La pregunta original era sobre la importación condicional solamente, sin usar marcadores de posición. Entonces esta es realmente la respuesta correcta. – Jakub

Cuestiones relacionadas