web-dev-qa-db-fra.com

Utilisation correcte des profils d'environnement Spring afin de gérer PropertySourcesPlaceholderConfigurer et des ensembles de fichiers de propriétés

Je travaille sur une application Spring et je me rends compte que j'ai un problème avec la façon dont je gère mes propriétés. J'utilise des profils d’environnement Spring afin de charger mes propriétés et j’ai récemment ajouté d’autres profils qui ont rendu mes fichiers de propriétés impossibles à gérer .

Les fichiers de propriétés sont situés dans différents dossiers dans src/main/resources/META-INF/props/, chaque dossier correspondant à un profil d'environnement Spring différent. 

J'ai au moins 5 profils maintenant, ce qui signifie que j'ai 5 sous-dossiers contenant chacun les fichiers de propriétés avec les mêmes noms mais avec des valeurs différentes pour seulement certaines clés .

Voici à quoi ça ressemble:

properties file screen capture

Voici comment j'ai configuré mes PropertyPlaceholders:

@Configuration
public class PropertyPlaceholderConfiguration {

    @Profile(Profiles.CLOUD)
    static class cloudConfiguration {
        @Bean
        public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
            PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
            propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
            propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/cloud/*.properties"));
            return propertySourcesPlaceholderConfigurer;
        }
    }

    @Profile(Profiles.DEFAULT)
    static class defaultConfiguration {
        @Bean
        public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
            PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
            propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
            propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/default/*.properties"));
            return propertySourcesPlaceholderConfigurer;
        }
    }

    @Profile(Profiles.TEST)
    static class testConfiguration {
        @Bean
        public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
            PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
            propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
            propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/test/*.properties"));
            return propertySourcesPlaceholderConfigurer;
        }
    }

    @Profile(Profiles.DEV)
    static class devConfiguration {
        @Bean
        public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws IOException {
            PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
            propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(Boolean.TRUE);
            propertySourcesPlaceholderConfigurer.setLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:META-INF/props/dev/*.properties"));
            return propertySourcesPlaceholderConfigurer;
        }
     ...
    }

En résumé, mon problème est le suivant:

  • les paires clé/valeur sont dupliquées dans les 5 dossiers différents car seules quelques valeurs sont différentes. 

Je recherche donc une nouvelle stratégie pour gérer les différences entre les différents environnements. 

Quelqu'un peut-il aider s'il vous plait?

14
balteo

Il y a plusieurs façons de le faire mais je pense que vous êtes sur le bon chemin. Le remplacement des fichiers de propriétés s'effectue par le biais de BeanFactoryPostProcessors, et il existe deux implémentations qui peuvent vous aider dans ce cas, vous évitant ainsi de recommencer à zéro:

PropertySourcesPlaceholderConfigurer & PropertyOverrideConfigurer.

Ceci est un exemple utilisant PropertySourcesPlaceholderConfigurer:

<bean id="someProperties" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer" abstract="true" >
    <property name="locations">
        <list>
            <value>classpath:database.properties</value>
            <value>classpath:email.properties</value>
        </list>
    </property>
    <property name="ignoreUnresolvablePlaceholders" value="false"/>
</bean>

<bean id="devProperties" parent="someProperties"  >
    <property name="properties" >
        <props >
            <prop key="database.username">Database Username used for Development Environment </prop> 
        </props>
    </property>
    <property name="localOverride" value="true" />
</bean>

<bean id="testProperties" parent="someProperties"  >
    <property name="properties" >
        <props >
            <prop key="database.username">Database Username used for Testing Environment </prop> 
        </props>
    </property>
    <property name="localOverride" value="true" />
</bean>

Dans cet exemple, vous chargez les propriétés par défaut dans un bean qui servira de modèle pour d'autres beans. Dans le bean spécifique, par exemple, testEnvironmentProperties Bean ou DevEnvironmentProperties Bean, vous remplacez uniquement les propriétés spécifiques que vous souhaitez remplacer par les fichiers de propriétés par défaut. L'exemple montre uniquement comment remplacer des propriétés spécifiques sans qu'il soit nécessaire de créer un autre fichier de propriétés. À partir de là, vous pourrez choisir le bean à créer avec une usine, une simple classe de façade ou un système de profils. architecture.

De plus, si vous pensez que cette option est trop détaillée, vous pouvez utiliser l'élément property-placeholder .

Je vous recommande ce livre:

Premiers pas avec Spring Framework, Deuxième édition

il ne contient que les exemples dont vous avez besoin dans son cinquième chapitre. Je ne l'ai pas écrit ou quoi que ce soit, je l'ai juste acheté il y a quelque temps et je l'ai aimé après avoir parcouru tant de mauvais livres de printemps.

5
Langley

Extrayez les propriétés communes dans un fichier séparé et spécifiez-le, ainsi que les propriétés spécifiques du profil en tant qu'entrées pour chaque profil. Je n'ai pas utilisé la configuration Spring basée sur Java, mais voici comment je le fais en XML. Supposons que vous puissiez faire la même chose dans le code:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <beans profile="default">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/common.profile.properties</value>
                    <value>classpath:profiles/local.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    <beans profile="local">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/common.profile.properties</value>
                    <value>classpath:profiles/local.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    <beans profile="trial">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/common.profile.properties</value>
                    <value>classpath:profiles/trial.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

    <beans profile="live">
        <bean id="applicationPropertiesPlaceholder"
            class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="locations">
                <list>
                    <value>classpath:profiles/common.profile.properties</value>
                    <value>classpath:profiles/live.profile.properties</value>
                </list>
            </property>
        </bean>
    </beans>

</beans>
2
Alan Hay

La meilleure pratique consiste à placer tous les fichiers de propriétés en dehors du package WAR. Vous pouvez utiliser une variable JNDI pour faire pointer Spring sur le chemin physique où les fichiers de propriétés externes peuvent être lus. Exemple: 

<jee:jndi-lookup id="externalFileArea" jndi-name="Java:comp/env/mgo/externalFileArea"
                     default-value="/opt/external/props" lookup-on-startup="true"/>

<util:properties id="myConf" location="file:#{externalFileArea}/my-conf.properties"/>

<!-- And now, to use an entry from this properties import -->
<bean id="foo" class="foo.bar.com">
     <property name="configParam1" value="#{myConf['fooConfig.param1']}"
</bean>

Si sous Windows, l'entrée JNDI peut être spécifiée sous la forme/C/Users/someone.Enfin, ajoutez un fichier nommé /opt/external/props/my-conf.properties et placez-y une entrée du type: .param1 = true

Laver, rincer, répéter. Beaucoup moins de travail, beaucoup plus sécurisé et beaucoup plus facile à entretenir.

0
Jim Doyle

Je pense que je suis tombé sur le début d’une solution avec ce blog intéressant .

Pour citer l'article:

Attention à la redondance des propriétés spécifiques à l'environnement. Par exemple, si la solution consiste à avoir un fichier de propriétés pour chaque environnement (par exemple, «db-test.properties», «db-dev.properties», etc.), puis Le maintien de ces propriétés peut être un peu un cauchemar - si un la propriété "foo" est ajoutée, elle devra alors être ajoutée à la fichier de propriétés pour chaque environnement (par exemple, DEV, TEST, PROD, etc.). Le PropertyOverrideConfigurer est approprié pour éliminer cela redondance, définition de la valeur par défaut dans le contexte de l'application lui-même, mais ensuite la valeur prépondérante dans un fichier séparé. Ses Cependant, il est important de bien documenter cela, car cela peut paraître un peu "Magique" à un développeur de maintenance peu méfiant qui ne voit qu'une valeur spécifié dans le fichier de contexte, mais un autre utilisé au moment de l'exécution.

L'idée est de s'appuyer sur PropertyOverrideConfigurer } et de factoriser les propriétés communes.

0
balteo

Je suggérerais que les propriétés "communes" ne doivent pas obligatoirement figurer dans un fichier commun, mais peuvent plutôt être des valeurs par défaut d'espace réservé aux propriétés en ligne dans votre code. Cela leur permet d'être remplacés via les arguments de la machine virtuelle Java (ou env locale) sans qu'il soit nécessaire de les "gérer" dans un fichier. Vos propriétés spécifiques à l'environnement, dans vos fichiers spécifiques à l'environnement, indiquent uniquement les propriétés qui DOIVENT être fournies dans chaque environnement pour que l'application démarre. En tant que tels, ils n'auraient PAS de valeurs par défaut dans les espaces réservés.

0
Michael Andrews