¿Cómo lograr la importación de recursos condicional en un contexto Spring XML?

Lo que me gustaría lograr es la capacidad de “dinámicamente” (es decir, basado en una propiedad definida en un archivo de configuración) activar / desactivar la importación de un contexto Spring XML infantil.

Me imagino algo así como:

 

Donde la propiedad se resuelve (a un valor booleano) y cuando es verdadero, el contexto se importa; de lo contrario, no lo es.

Algunas de mis investigaciones hasta el momento:

  • Escribir un NamespaceHandler personalizado (y clases relacionadas) para poder registrar mi propio elemento personalizado en mi propio espacio de nombres. Por ejemplo:

    El problema con este enfoque es que no quiero replicar toda la lógica de importación de recursos de Spring y no me resulta obvio a qué tengo que delegar para hacer esto.

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

Lo más cercano que puede obtener con los componentes estándar de Spring es:

  

donde ${xyzzy} interpola una propiedad desde las propiedades del sistema. (Utilizo una versión personalizada de hacky 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 usar varios trucos para causar la creación de instancias de los beans necesarios. Estos trucos incluyen:

  • marcador de posición y sustitución de propiedad
  • seleccionando diferentes beans usando el nuevo lenguaje de expresión de Spring,
  • alias de beans con marcadores de posición en el nombre del objective,
  • inicialización de beans perezosos, y
  • fábricas de judías inteligentes.

Con Spring 3.1.x puede usar perfiles de bean para lograr la importación de recursos condicional y la creación de instancias de bean. Por supuesto, esto no sirve de nada si estás usando una versión anterior 🙂

Como se mencionó anteriormente, esto se puede lograr fácilmente con perfiles si está utilizando Spring 3.1+

           

otherProfile se puede activar fácilmente con, por ejemplo,

 mvn install -Dspring.profiles.active=otherProfile 

Si usa diferentes perfiles en las pruebas, simplemente agregue -DforkMode=never para asegurarse de que las pruebas se ejecutarán dentro de la misma máquina virtual, por lo tanto, el spring.profiles.active no se perderá.

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/condicionally-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 con este enfoque, adaptado para su ejemplo, es:

    

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

Esto ahora es completamente posible, usando Spring 4.

En su archivo de contenido principal de la aplicación

  

Y el MyConditionalConfiguration se ve como

 @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 finalmente, coloca las definiciones de bean que desea incluir en el archivo /com/example/context-fragment.xml

Ver JavaDoc for @Conditional

Otra opción es hacer que su aplicación cargue un archivo modules-config.xml que se encuentra en la carpeta / conf y editarlo 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 para cargarlo.

Otro a considerar para Spring 3.0:

   

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

Puede anular contextInitized (evento javax.servlet.ServletContextEvent) en su propio ContextLoaderListener y establecer la propiedad del sistema requerida antes de que se invoque super.contextInitialized (event) como este

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

Y que reemplazar ContextLoaderListener a MyContextLoaderListener en su web.xml

  com.mypackage.MyContextLoaderListener  

Ahora puedes usar en tu spring.xml

  

Espero que esto sea de ayuda.