13

Sto lavorando a un'applicazione Spring e mi sto rendendo conto di avere un problema con il modo in cui gestisco le mie proprietà. Uso i profili di ambiente Spring per caricare le mie proprietà e recentemente ho aggiunto altri profili che hanno reso i miei file di proprietà non gestibili.Utilizzare correttamente i profili di ambiente Spring per gestire PropertySourcesPlaceholderConfigurer e set di file di proprietà

I file delle proprietà si trovano in diverse cartelle all'interno di src/main/resources/META-INF/props/, con la cartella eah che corrisponde a un diverso profilo ambiente Spring.

Ho almeno 5 profili ora che significa che ho 5 sottocartelle ciascuna contenente i file delle proprietà con lo stesso nome ma con valori diversi per solo alcuni tasti.

Ecco come appare:

properties file screen capture

Ecco come ho configurato il mio 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; 
     } 
    ... 
    } 

Per riassumere, il mio problema è il seguente:

  • coppie chiave/valore sono duplicate in tutte le 5 cartelle diverse perché solo alcune val voi siete diversi

Sono quindi alla ricerca di una nuova strategia per gestire le differenze tra i diversi ambienti.

Qualcuno può aiutare per favore?

risposta

1

Estrarre le proprietà comuni in un file separato e specificare quello più le proprietà specifiche del profilo come input per ciascun profilo. Non ho usato la configurazione di Spring con Java, ma ecco come faccio in XML. Si supponga che si può fare lo stesso in codice:

<?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> 
+0

Grazie Alan! Ummm, l'unica cosa è che la tua forza costringe uno ad avere proprietà non correlate nello stesso file (cioè common.profile.properties) ... – balteo

+0

Un altro problema con la soluzione suggerita è che se per qualche motivo ad un certo punto ho bisogno di valori diversi per una delle proprietà ho bisogno di estrarlo dal file comune e metterlo nei file specifici del profilo. – balteo

0

Credo che mi sono imbattuto l'inizio di una soluzione con questo interesting blog post.

Per citare l'articolo:

Attenzione della ridondanza delle proprietà specifiche dell'ambiente. Ad esempio, se la soluzione deve avere un file di proprietà per ciascun ambiente (ad esempio "db-test.properties", "db-dev.properties", ecc.), Quindi il mantenendo queste proprietà può essere un po ' incubo: se viene aggiunta una proprietà "foo" , sarà necessario aggiungerla al file di proprietà per ogni ambiente (ad esempio DEV, TEST, PROD, ecc.). Il PropertyOverrideConfigurer è appropriato per eliminare questa ridondanza , impostando il valore predefinito nel contesto dell'applicazione stesso, ma successivamente il valore di priorità in un file separato. È importante, tuttavia, documentarlo, poiché può sembrare un po '"magico" a uno sviluppatore di manutenzione ignaro che vede un valore specificato nel file di contesto, ma un altro utilizzato in fase di esecuzione.

L'idea è di fare affidamento su PropertyOverrideConfigurer e scomporre proprietà comuni.

4

Ci sono molti modi per farlo, ma penso che tu sia sulla strada giusta. imperativi di file di proprietà viene fatto attraverso BeanFactoryPostProcessors, e non c'è due implementazioni che possono aiutare in questo caso in modo da non dovete fare nuovamente da zero:

PropertySourcesPlaceholderConfigurer & PropertyOverrideConfigurer.

Questo è un esempio usando 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> 

In questo esempio, si caricano le proprietà di default in un fagiolo che verrà utilizzato come modello per altri fagioli e nel chicco specifica, dici TestEnvironmentProperties Bean o DevEnvironmentProperties Bean si esegue l'override solo delle proprietà specifiche che si desidera sovrascrivere dai file delle proprietà predefinite. L'esempio mostra solo come sovrascrivere proprietà specifiche senza la necessità di creare un altro file di proprietà, da lì puoi decidere come scegliere quale bean creare con una factory, una semplice classe di facciata o un sistema di profili, tutto ciò che ti piace e corrisponde al tuo architettura.

Inoltre, se si ritiene che questa opzione sia troppo prolissa, è possibile utilizzare l'elemento property-placeholder.

vi consiglio questo libro:

Getting started with Spring Framework, Second Edition

ha solo gli esempi è necessario nella sua 5 ° capitolo. Non l'ho scritto o altro, l'ho appena comprato qualche tempo fa e l'ho adorato dopo aver attraversato così tanti cattivi libri di primavera.

0

La pratica migliore consiste nel mettere tutti i file delle proprietà all'esterno della confezione di WAR. È possibile utilizzare una variabile JNDI per puntare Spring al percorso fisico in cui è possibile leggere i file delle proprietà esterne. Esempio:

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

Se in Windows, la voce JNDI può essere specificata come/C/Utenti/qualcuno. Infine, aggiungi un file denominato /opt/external/props/my-conf.properties, e in esso inserisci una voce come: fooConfig.param1 = true

Lavare, risciacquare, ripetere. Molto meno lavoro, molto più sicuro e molto più facile da mantenere.

0

Suggerirei che le proprietà "comuni" non devono essere in un file comune e possono invece essere valori predefiniti del segnaposto di proprietà in linea nel codice. Ciò consente loro di essere sovrascritti tramite Args JVM (o env locale) senza dover essere "gestiti" in un file. Le proprietà specifiche dell'ambiente, nei file specifici dell'ambiente, quindi indicano solo le proprietà che DEVONO essere fornite in ogni ambiente per l'avvio dell'app. In quanto tali, NON avrebbero valori predefiniti nei segnaposto.