Ce que j'aimerais réaliser, c’est la possibilité d’activer/désactiver "dynamiquement" (c’est-à-dire selon une propriété définie dans un fichier de configuration) l’importation d’un contexte XML Spring enfant.
J'imagine quelque chose comme:
<import condition="some.property.name" resource="some-context.xml"/>
Lorsque la propriété est résolue (en un booléen) et lorsque la valeur est true, le contexte est importé, sinon il ne l'est pas.
Quelques unes de mes recherches jusqu'à présent:
Écriture d'un NamespaceHandler personnalisé (et des classes associées) afin que je puisse enregistrer mon propre élément personnalisé dans mon propre espace de noms. Par exemple: <myns:import condition="some.property.name" resource="some-context.xml"/>
Le problème avec cette approche est que je ne souhaite pas reproduire la logique d'importation de ressources dans son intégralité à partir de Spring et je ne vois pas très bien à quoi me confier cette tâche.
DefaultBeanDefinitionDocumentReader
pour étendre le comportement de l'analyse 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.C’est maintenant tout à fait possible avec Spring 4.
Dans le fichier de contenu de votre application principale
<bean class="com.example.MyConditionalConfiguration"/>
Et le 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 finalement, vous mettez les définitions de beans que vous voulez inclure dans /com/example/context-fragment.xml
Voir le JavaDoc pour @Conditional
Le plus proche que vous pouvez obtenir en utilisant des composants Spring standard est:
<import resource="Whatever-${yyzzy}.xml"/>
où ${xyzzy}
interpole une propriété à partir des propriétés du système. (J'utilise une version personnalisée hacky de la classe de chargeur de contexte qui ajoute des propriétés d'autres emplacements à l'objet de propriétés système avant de lancer le processus de chargement.)
Mais vous pouvez également vous contenter d'importer beaucoup de choses inutiles ... et utiliser diverses astuces pour provoquer uniquement l'instanciation des beans nécessaires. Ces astuces incluent:
Avec Spring 3.1.x, vous pouvez utiliser profiles de beans pour réaliser une importation conditionnelle des ressources et une instanciation de bean. Ceci est bien sûr sans aide si vous utilisez une version antérieure :)
Comme mentionné précédemment, ceci peut être facilement réalisé avec des profils si vous utilisez 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 peut être facilement activé avec par ex.
mvn install -Dspring.profiles.active=otherProfile
si vous utilisez des profils différents dans les tests, ajoutez simplement -DforkMode=never
pour vous assurer que les tests s'exécutent dans la même machine virtuelle. Par conséquent, le paramètre 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 . C'est un peu long de le copier ici (d'ailleurs, je ne pense pas que je devrais quand même copier-coller son article).
Le résultat final avec cette approche, adaptée à votre exemple, est:
<condbean:cond test="${some.property.name}">
<import resource="some-context.xml"/>
</condbean:cond>
Ce n’est certes pas aussi simple que la solution de Stephen C, mais c’est beaucoup plus puissant.
Vous pouvez remplacer l'événement contextInitialized (événement javax.servlet.ServletContextEvent) dans votre propre ContextLoaderListener et définir la propriété système requise avant que super.contextInitialized (événement) 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
<listener>
<listener-class>com.mypackage.MyContextLoaderListener</listener-class>
</listener>
Maintenant, vous pouvez utiliser dans votre spring.xml
<import resource="${xyz}" />
J'espère que cela aidera.
Un autre à considérer pour Spring 3.0:
<alias name="Whatever" alias=""Whatever-${yyzzy}" />
où ${xyzzy}
interpole une propriété à partir des propriétés du système.
Une autre option consiste à charger votre application d'un fichier modules-config.xml situé dans le dossier/conf et à le modifier au cours de la phase d'installation/config pour supprimer le commentaire des modules que vous souhaitez 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 distribuée avec tous les différents modules d'intégration. Un module-config.xml est placé dans le dossier/conf de Tomcat et le dossier conf est ajouté au chemin de classe (via la propriété catalina.properties/common.loader). Mon application Web webapp-config.xml a un <import resource="classpath:/modules-config.xml"/>
pour le charger.