web-dev-qa-db-fra.com

Comment réaliser une importation de ressources conditionnelle dans un contexte XML Spring?

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.

  • Remplacement de 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.
53
Boris Terzic

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

21
ptomli

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

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

${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:

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

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 :)

22
teabot

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.

22
Marko Vranjkovic

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.

20
espinchi

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.

0
szezso

Un autre à considérer pour Spring 3.0:

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

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

0
hawkeye

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.

0
ScottD