Comment faire pour importer des ressources conditionnelles dans un contexte XML Spring?

Ce que je voudrais réaliser, c’est la possibilité de “dynamicment” (c’est-à-dire basée sur une propriété définie dans un fichier de configuration) d’activer / désactiver l’importation d’un contexte XML enfant.

J’imagine quelque chose comme:

 

Où la propriété est résolue (à une valeur booléenne) et à quel moment le contexte est importé, sinon ce n’est pas le cas.

Certaines de mes recherches à ce jour:

  • Écrire un NamespaceHandler personnalisé (et les classes associées) afin de pouvoir enregistrer mon propre élément personnalisé dans mon propre espace de noms. Par exemple:

    Le problème avec cette approche est que je ne veux pas reproduire l’intégralité de la logique d’importation de ressources à partir de Spring et il n’est pas évident que je doive déléguer cette tâche.

  • Remplacement de DefaultBeanDefinitionDocumentReader pour étendre le comportement de l’parsing et de l’interprétation de l’élément “import” (ce qui se produit dans la méthode importBeanDefinitionResource ). Cependant, je ne sais pas où je peux enregistrer cette extension.

Le plus proche que vous pouvez obtenir en utilisant des composants Spring standard est:

  

${xyzzy} interpole une propriété des propriétés du système. (J’utilise une version personnalisée de hacky de la classe du chargeur de contexte qui ajoute des propriétés à d’autres endroits à l’object de propriétés du système avant de lancer le processus de chargement.)

Mais vous pouvez également importer beaucoup de choses inutiles … et utiliser différentes astuces pour provoquer l’instanciation des beans nécessaires. Ces astuces incluent:

  • substitution d’espace réservé et de propriété
  • sélectionner différents beans en utilisant le nouveau langage d’expression Spring,
  • des alias de haricot avec des espaces réservés dans le nom de la cible,
  • initialisation du haricot paresseux et
  • usines de haricots intelligents.

Avec Spring 3.1.x, vous pouvez utiliser des profils de bean pour obtenir une importation de ressources conditionnelle et une instanciation de bean. Cela ne sert à rien si vous utilisez une version antérieure 🙂

Comme mentionné précédemment, cela peut être facilement réalisé avec des profils si vous utilisez Spring 3.1+

           

otherProfile peut être facilement activé avec par exemple

 mvn install -Dspring.profiles.active=otherProfile 

Si vous utilisez des profils différents dans les tests, ajoutez simplement -DforkMode=never pour -DforkMode=never vous assurer que les tests seront exécutés dans la même machine virtuelle. Par conséquent, le spring.profiles.active ne sera pas perdu.

Pour mémoire, Robert Maldon explique comment réaliser la définition conditionnelle des haricots dans cet article: http://robertmaldon.blogspot.com/2007/04/conditional-defining-spring-beans.html . Il est un peu long de le copier ici (d’ailleurs, je ne pense pas que je devrais copier-coller son article de toute façon).

Le résultat final de cette approche, adapté à votre exemple, est le suivant:

    

Ce n’est certainement pas aussi simple que la solution de Stephen C, mais c’est beaucoup plus puissant.

C’est maintenant tout à fait possible, en utilisant Spring 4.

Dans le fichier de contenu de votre application principale

  

Et la MyConditionalConfiguration ressemble à

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

Et puis enfin, vous mettez les définitions de beans que vous voulez inclure dans /com/example/context-fragment.xml

Voir le JavaDoc pour @Conditional

Une autre option consiste à demander à votre application de charger un fichier modules-config.xml situé dans le dossier / conf et de le modifier lors de la phase d’installation / configuration pour supprimer la mise en commentaire des modules à charger.

C’est la solution que j’utilise avec une application Web qui sert de conteneur pour différents modules d’intégration. L’application Web est dissortingbuée avec tous les différents modules d’intégration. Un fichier modules-config.xml est placé dans le dossier / conf de tomcat et le dossier conf est ajouté au classpath (via la propriété catalina.properties/common.loader). Mon application web webapp-config.xml a un chemin de classe pour le charger.

Un autre à considérer pour Spring 3.0:

   

${xyzzy} interpole une propriété des propriétés du système.

Vous pouvez remplacer contextInitialized (événement javax.servlet.ServletContextEvent) dans votre propre ContextLoaderListener et définir la propriété System requirejse avant que super.contextInitialized (event) soit appelé comme ceci

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

Et que remplacer ContextLoaderListener à MyContextLoaderListener dans votre web.xml

  com.mypackage.MyContextLoaderListener  

Maintenant, vous pouvez utiliser dans votre spring.xml

  

J’espère que cela aidera.