Dans ma configuration Spring xml, j'essaie de faire fonctionner quelque chose comme ceci:
<beans>
<import resource="${file.to.import}" />
<!-- Other bean definitions -->
</beans>
Je souhaite choisir le fichier à importer en fonction d'une propriété dans un fichier de propriétés . Je sais que je peux utiliser une propriété System, mais je ne peux pas ajouter de propriété à la machine virtuelle au démarrage.
Remarque: Le PropertyPlaceHolderConfigurer fonctionnera pas . Les importations sont résolues avant l'exécution des processeurs BeanFactoryPostProcessors. L'élément import peut uniquement résoudre System.properties.
Quelqu'un at-il une solution simple à cela? Je ne veux pas commencer à sous-classer les classes du framework, etc.
Merci
C'est malheureusement beaucoup plus difficile que cela ne devrait être. Dans ma candidature, j'ai accompli ceci en procédant comme suit:
Un petit contexte "d'amorçage" responsable du chargement d'un bean PropertyPlaceholderConfigurer et d'un autre bean responsable de l'amorçage du contexte de l'application.
Le deuxième bean mentionné ci-dessus prend en entrée les "vrais" fichiers de contexte de ressort à charger. J'ai mes fichiers de contexte Spring organisés de manière à ce que la partie configurable soit bien connue et se trouve au même endroit. Par exemple, je pourrais avoir 3 fichiers de configuration: un.onpremise.xml, un.hosted.xml, un.multitenant.xml. Le bean charge par programme ces fichiers de contexte dans le contexte d'application actuel.
Cela fonctionne car les fichiers de contexte sont spécifiés en tant qu'entrée du bean responsable de leur chargement. Cela ne fonctionnera pas si vous essayez simplement de faire une importation, comme vous l'avez mentionné, mais cela aura le même effet avec un peu plus de travail. La classe bootstrap ressemble à ceci:
public class Bootstrapper implements ApplicationContextAware, InitializingBean {
private WebApplicationContext context;
private String[] configLocations;
private String[] testConfigLocations;
private boolean loadTestConfigurations;
public void setConfigLocations(final String[] configLocations) {
this.configLocations = configLocations;
}
public void setTestConfigLocations(final String[] testConfigLocations) {
this.testConfigLocations = testConfigLocations;
}
public void setLoadTestConfigurations(final boolean loadTestConfigurations) {
this.loadTestConfigurations = loadTestConfigurations;
}
@Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
context = (WebApplicationContext) applicationContext;
}
@Override
public void afterPropertiesSet() throws Exception {
String[] configsToLoad = configLocations;
if (loadTestConfigurations) {
configsToLoad = new String[configLocations.length + testConfigLocations.length];
arraycopy(configLocations, 0, configsToLoad, 0, configLocations.length);
arraycopy(testConfigLocations, 0, configsToLoad, configLocations.length, testConfigLocations.length);
}
context.setConfigLocations(configsToLoad);
context.refresh();
}
}
Fondamentalement, obtenez le contexte de l'application, définissez ses emplacements de configuration et dites-lui de se rafraîchir. Cela fonctionne parfaitement dans mon application.
J'espère que cela t'aides.
Pour les versions Spring 2.5 et 3.0, j'ai une solution similaire à Louis, mais je viens de lire un article sur la prochaine fonctionnalité de 3.1: la gestion de la propriété , qui sonne très bien aussi.
Un problème ancien lié à l'ajout de de la réservation d'espace réservé aux propriétés pour l'importation (SPR-1358) a été résolu et résolu comme "ne sera pas résolu", mais une solution proposée depuis utilise un EagerPropertyPlaceholderConfigurer.
J'ai fait pression pour que SPR-1358 soit rouvert, mais aucune réponse à ce jour. Peut-être que si d’autres ajoutaient leurs cas d’utilisation aux commentaires relatifs à la question, cela contribuerait à sensibiliser la population.
Pourquoi pas:
vous êtes donc en train d'inverser votre solution proposée actuelle.
Ajoutez quelque chose de similaire au suivant:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound"><value>true</value></property>
<property name="locations">
<list>
<value>classpath:propertyfile.properties</value>
</list>
</property>
</bean>
Si vous voulez spécifier le nom du fichier XML importé en dehors de applicationContext.xml afin de pouvoir remplacer applicationContext.xml sans perdre la configuration du chemin du fichier XML importé, vous pouvez simplement ajouter un fichier XML Spring beans intermédiaire, par exemple, confSelector. xml, de sorte que applicationContext.xml importe confSelector.xml et confSelector.xml uniquement un élément <import> faisant référence au fichier XML de beans personnalisé approprié.
Les entités XML (définies en ajoutant des éléments <! ENTITY ...> dans la déclaration DTD au début de XML) peuvent également être utiles. Celles-ci permettent d'importer des fragments XML à partir d'autres fichiers et fournissent une fonctionnalité de type "espace réservé de propriété" pour tout fichier XML.
Aucune de ces solutions ne vous permet cependant d’avoir le fichier de configuration au format .properties de Java.
Une autre solution de contournement qui ne repose pas sur les propriétés système consiste à charger les propriétés de tous les fichiers en utilisant un PropertyPlaceholderConfigurer différent pour chaque fichier et à définir un paramètre placeholderPrefix différent pour chacun d'entre eux .
Définissez le premier fichier de propriétés: (contenant le premier ou le second)
fileToUse=first
Définissez les fichiers contenant une propriété qui peut être modifiée en fonction de la propriété définie ci-dessus:
aProperty=propertyContentOfFirst
aProperty=propertyContentOfSecond
Ensuite, définissez les espaces réservés pour tous les fichiers:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:global.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="placeholderPrefix" value="first{" />
<property name="locations">
<list>
<value>classpath:first.properties</value>
</list>
</property>
</bean>
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="placeholderPrefix" value="second{" />
<property name="locations">
<list>
<value>classpath:second.properties</value>
</list>
</property>
</bean>
Utilisez la propriété définie dans global pour identifier la ressource à utiliser à partir de l'autre fichier:
${fileToUse}{aProperty}
Si j'ajoute l'argument JVM ci-dessous et que le fichier est myApplicationContext.dev.xml, Spring est chargé
-DmyEnvironment = dev
<context:property-placeholder />
<import resource="classpath:/resources/spring/myApplicationContext.${myEnvironment}.xml"/>
La réponse d’André Schuster, que j’ai ratée, m’a aidé à résoudre un problème très similaire: tenter de trouver une expression différente des propriétés en fonction de mon exécution sur mon propre hôte, de Jenkins sur notre hôte de construction ou dans un déploiement "réel". . J'ai fait ça:
<context:property-placeholder location="file:///etc/myplace/database.properties" />
suivi plus tard par
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>WEB-INF/classes/resources/database.properties</value>
...
</list>
</property>
</bean>
ce qui a résolu mon problème car sur mon hôte de développement, je mets un lien vers ma propre copie de database.properties in /etc/myplace/database.properties , et légèrement différente sur le serveur exécutant Jenkins. . En cas de déploiement réel, aucun fichier de ce type n’est trouvé, aussi Spring retombe-t-il sur le "vrai" sous-répertoire de resources in my class files. Si les propriétés en question ont déjà été spécifiées par le fichier sur /etc/myplace/database.properties , alors (heureusement) elles ne sont pas redéfinies par le fichier local.