Utilizzo della variabile env in application.properties di Spring Boot


201

Stiamo lavorando su un'app Web Spring Boot e il database che stiamo utilizzando è MySql ;

  • la configurazione che abbiamo è prima testarla localmente (significa che dobbiamo installare MySql sul nostro PC);

  • quindi passiamo a Bitbucket ;

  • Jenkins rileva automaticamente la nuova spinta su Bitbucket e crea una build su di essa (per far passare Jenkins mvn build dobbiamo anche installare MySql sulle macchine virtuali che eseguono Jenkins).

  • se la build Jenkins passa, inviamo il codice alla nostra applicazione su OpenShift (usando il plug-in di distribuzione Openshift su Jenkins).

Il problema che abbiamo come potresti aver già capito è che:

  • in application.propertiesnon è possibile codificare le informazioni di MySql. Poiché il nostro progetto verrà eseguito in 3 luoghi diversi ( locale , Jenkins e OpenShift ), dobbiamo rendere dinamico il campo dell'origine dati application.properties(sappiamo che ci sono diversi modi per farlo, ma per ora stiamo lavorando su questa soluzione).

    spring.datasource.url = 
    spring.datasource.username = 
    spring.datasource.password = 

La soluzione che abbiamo trovato è creare variabili di ambiente di sistema localmente e in Jenkins vm (nominandole nello stesso modo in cui OpenShift le nomina) e assegnando loro i valori giusti rispettivamente:

export OPENSHIFT_MYSQL_DB_HOST="jdbc:mysql://localhost"
export OPENSHIFT_MYSQL_DB_PORT="3306"
export OPENSHIFT_MYSQL_DB_USERNAME="root"
export OPENSHIFT_MYSQL_DB_PASSWORD="123asd"

L'abbiamo fatto e funziona. Abbiamo anche verificato Map<String, String> env = System.getenv();che le variabili di ambiente possano essere trasformate in variabili java in quanto tali:

String password = env.get("OPENSHIFT_MYSQL_DB_PASSWORD");   
String userName = env.get("OPENSHIFT_MYSQL_DB_USERNAME");   
String sqlURL = env.get("OPENSHIFT_MYSQL_DB_HOST"); 
String sqlPort = env.get("OPENSHIFT_MYSQL_DB_PORT");

Ora l'unica cosa rimasta è che dobbiamo usare queste variabili java nelle nostre application.propertiese questo è ciò con cui abbiamo problemi.

In quale cartella, e in che modo, abbiamo bisogno di assegnare i password, userName, sqlURL, e sqlPortvariabili per application.propertiesessere in grado di vederli e come possiamo includerli nella application.properties?

Abbiamo provato molte cose, una delle quali è:

spring.datasource.url = ${sqlURL}:${sqlPort}/"nameofDB"
spring.datasource.username = ${userName}
spring.datasource.password = ${password}

Nessuna fortuna finora. Probabilmente non inseriremo queste variabili env nella giusta classe / cartella o le stiamo usando in modo errato application.properties.

Il tuo aiuto è molto apprezzato !!

Grazie!


3
Leggi @ConfigurationProperties per saperne di più. Tuttavia, questo è un caso d'uso perfetto per le proprietà di configurazione specifiche del profilo
Eddie B,

Risposte:


271

Non è necessario utilizzare le variabili java. Per includere le variabili env di sistema aggiungi quanto segue al tuo application.propertiesfile:

spring.datasource.url = ${OPENSHIFT_MYSQL_DB_HOST}:${OPENSHIFT_MYSQL_DB_PORT}/"nameofDB"
spring.datasource.username = ${OPENSHIFT_MYSQL_DB_USERNAME}
spring.datasource.password = ${OPENSHIFT_MYSQL_DB_PASSWORD}

Ma il modo suggerito da @Stefan Isele è più preferibile, perché in questo caso si deve dichiarare una sola variabile env: spring.profiles.active. Spring leggerà automaticamente il file di proprietà appropriato per application-{profile-name}.propertiesmodello.


12
Questo metodo è più conveniente per il collegamento docker. Ad esempio:docker run --name my-tomcat -p 127.0.0.1:8080:8080 -e APP_DB_DB=mydb -e APP_DB_USER=dbuser -e APP_DB_PASS=dbpass --link mongo-myapp:mongo -v /path-to/tomcat/webapps:/usr/local/tomcat/webapps -d tomcat:8-jre8-alpine
Fırat KÜÇÜK,

17
Questo è assolutamente il modo migliore per andare. L'uso delle variabili di ambiente significa che non è necessario elencare i segreti in testo semplice accanto all'applicazione. Ciò è significativamente più sicuro e riduce la dipendenza dalle misure di sicurezza dell'accesso al codice sorgente per proteggere l'intera proprietà. Un post SO accidentale con proprietà incluse non comporta la perdita di informazioni.
kipper_t

51
Volevo aggiungere a questo e menzionare che se si utilizza l'avvio di primavera (non ha verificato se funziona senza avvio), allora qualsiasi proprietà può essere sostituita automaticamente tramite una variabile di ambiente senza modificare application.properties. vale a dire, se si dispone di una proprietà chiamata spring.activemq.broker-urlpoi la variabile d'ambiente corrispondente sarebbe: SPRING_ACTIVEMQ_BROKER_URL. i punti e i trattini vengono automaticamente convertiti in caratteri di sottolineatura. Questo è estremamente conveniente quando si lavora con contenitori / stivale a molla.
abe

15
Se si progetta per il cloud non è un modo preferibile di utilizzare i profili Spring. L'uso delle variabili d'ambiente è raccomandato dallo standard dell'app a 12 fattori: 12factor.net/config
Mikhail Golubtsov

6
So che questo argomento è un po 'vecchio. Ma puoi combinare sia la configurazione variabile d'ambiente che la configurazione del profilo di primavera. Il tuo profilo di sviluppo dovrebbe avere informazioni statiche mentre il tuo profilo di produzione può utilizzare le variabili di ambiente. In questo modo gli sviluppatori non devono più definire le variabili di ambiente sulla propria macchina se vogliono solo distribuire il profilo di sviluppo.
underscore_05

72

Il modo più semplice per avere configurazioni diverse per ambienti diversi è utilizzare i profili a molla. Vedi configurazione esterna .

Questo ti dà molta flessibilità. Lo sto usando nei miei progetti ed è estremamente utile. Nel tuo caso avresti 3 profili: 'local', 'jenkins' e 'openshift'

Devi quindi file di proprietà specifica 3 profili: application-local.properties, application-jenkins.properties, eapplication-openshift.properties

Lì puoi impostare le proprietà per l'ambiente relativo. Quando esegui l'app devi specificare il profilo da attivare in questo modo: -Dspring.profiles.active=jenkins

modificare

Secondo il documento di primavera è possibile impostare la variabile di ambiente di sistema SPRING_PROFILES_ACTIVEper attivare i profili e non è necessario passarla come parametro.

c'è un modo per passare l'opzione del profilo attivo per l'app Web in fase di esecuzione?

No. Spring determina i profili attivi come uno dei primi passi, quando si crea il contesto dell'applicazione. I profili attivi vengono quindi utilizzati per decidere quali file delle proprietà vengono letti e quali bean vengono istanziati. Una volta avviata l'applicazione, questa non può essere modificata.


4
Mi piace questa risposta, ma cosa succede se si desidera che il nome del profilo provenga dall'ambiente? Ho provato -Dspring.active.profiles = $ SPRING_ACTIVE_PROFILES e impostato il var del sistema operativo in /etc/profile.d/myenvvars.sh, ma Spring Boot non lo rileva
Tom Hartwell,

1
SPRING_PROFILES_ACTIVE funziona a causa della rilassata funzionalità di associazione di Spring Boot docs.spring.io/spring-boot/docs/1.3.0.BUILD-SNAPSHOT/reference/…
feed.me

5
grazie per questa risposta Stefan, ha funzionato per me, ma con una modifica - la proprietà è in realtà spring.profiles.active e non spring.active.profiles
Rudi,

11
Mentre i profili a molla possono essere molto utili, in relazione all'OP non sono adatti. Ciò è dovuto al modo in cui il codice sorgente viene archiviato e alla sensibilità delle informazioni sulle proprietà memorizzate con quello. Il contesto OP riguarda l'accesso al database. Per quella situazione non si desidera fornire i dettagli in testo normale nella fonte. Ciò significa che se l'origine è compromessa, anche il database è compromesso. È meglio usare variabili env o strumenti segreti per questo invece come Vault. Preferisco env. Farei in modo che tutti gli ambienti funzionino allo stesso modo in questo senso per coerenza. Evita incidenti in futuro.
kipper_t

2
È possibile utilizzare un file delle proprietà del profilo Spring Boot esterno al JAR dell'applicazione. Questo file specifico dell'ambiente, ad esempio, application-production.propertiesverrebbe distribuito alla macchina di produzione in modo sicuro e non si troverebbe in genere nel repository del codice sorgente dell'applicazione.
Colin D Bennett,

13

Questo è in risposta a una serie di commenti in quanto la mia reputazione non è abbastanza elevata da commentare direttamente.

È possibile specificare il profilo in fase di esecuzione fintanto che il contesto dell'applicazione non è stato ancora caricato.

// Previous answers incorrectly used "spring.active.profiles" instead of
// "spring.profiles.active" (as noted in the comments).
// Use AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME to avoid this mistake.

System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, environment);
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/applicationContext.xml");

12

Flayway non riconosce le variabili di ambiente dirette in application.properties (Spring-Boot V2.1). per esempio

spring.datasource.url=jdbc:mysql://${DB_HOSTNAME}:${DB_PORT}/${DB_DATABASE}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASS}

Per risolvere questo problema ho fatto queste variabili d'ambiente, di solito creo il file .env:

SPRING_DATASOURCE_URL=jdbc:mysql://127.0.0.1:3306/place
SPRING_DATASOURCE_USERNAME=root
SPRING_DATASOURCE_PASSWORD=root

Ed esporta le variabili nel mio ambiente:

export $(cat .env | xargs)

E infine esegui il comando

mvn spring-boot:run

O esegui il tuo file jar

java -jar target/your-file.jar

C'è un altro approccio qui: https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/maven-plugin/examples/run-env-variables.html


1
Che cos'è env-vars? Come vengono usati La tua risposta si riferisce a cose senza una descrizione completa e non includi alcun link. Ho quasi annullato la votazione, ma vedo che il tuo rappresentante ha 21 anni, quindi sei nuovo e una persona ha trovato utile la tua risposta, quindi l'ho lasciata andare, ma provo a fornire ulteriori informazioni nelle risposte future e benvenuto in SO (Stack Overflow). Spero che ti piaccia tanto quanto me.
PatS

2
Grazie @PatS, ho aggiunto ulteriori dettagli, spero che sia utile.
Felipe Girotti,

1
Ottimi cambiamenti. Grazie per aver aggiornato la tua risposta.
PatS

9

Ecco un codice snippet attraverso una catena di ambienti proprietà file caricati per ambienti diversi.

File delle proprietà sotto le risorse dell'applicazione ( src / main / resources ): -

 1. application.properties
 2. application-dev.properties
 3. application-uat.properties
 4. application-prod.properties

Idealmente, application.properties contiene tutte le proprietà comuni che sono accessibili per tutti gli ambienti e le proprietà relative all'ambiente funzionano solo su specifica l'ambiente. pertanto l'ordine di caricamento di questi file delle proprietà sarà in questo modo -

 application.properties -> application.{spring.profiles.active}.properties.

Snippet di codice qui: -

    import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;

    public class PropertiesUtils {

        public static final String SPRING_PROFILES_ACTIVE = "spring.profiles.active";

        public static void initProperties() {
            String activeProfile = System.getProperty(SPRING_PROFILES_ACTIVE);
            if (activeProfile == null) {
                activeProfile = "dev";
            }
            PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer
                    = new PropertySourcesPlaceholderConfigurer();
            Resource[] resources = new ClassPathResource[]
                    {new ClassPathResource("application.properties"),
                            new ClassPathResource("application-" + activeProfile + ".properties")};
            propertySourcesPlaceholderConfigurer.setLocations(resources);

        }
    }

2
Spring Boot non gestisce questo scenario fuori dalla scatola? Vedi la documentazione di configurazione esterna qui
ChickenFeet

4

Forse lo scrivo troppo tardi, ma ho riscontrato il problema simile quando ho provato a ignorare i metodi per leggere le proprietà.

Il mio problema è stato: 1) Leggi la proprietà da env se questa proprietà è stata impostata in env 2) Leggi la proprietà dalla proprietà di sistema se questa proprietà è stata impostata nella proprietà di sistema 3) E, infine, leggi dalle proprietà dell'applicazione.

Quindi, per risolvere questo problema, vado alla mia classe di configurazione del bean

@Validated
@Configuration
@ConfigurationProperties(prefix = ApplicationConfiguration.PREFIX)
@PropertySource(value = "${application.properties.path}", factory = PropertySourceFactoryCustom.class)
@Data // lombok
public class ApplicationConfiguration {

    static final String PREFIX = "application";

    @NotBlank
    private String keysPath;

    @NotBlank
    private String publicKeyName;

    @NotNull
    private Long tokenTimeout;

    private Boolean devMode;

    public void setKeysPath(String keysPath) {
        this.keysPath = StringUtils.cleanPath(keysPath);
    }
}

E sovrascrivi factory in @PropertySource. E poi ho creato la mia implementazione per la lettura delle proprietà.

    public class PropertySourceFactoryCustom implements PropertySourceFactory {

        @Override
        public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
            return name != null ? new PropertySourceCustom(name, resource) : new PropertySourceCustom(resource);
        }


    }

E creato PropertySourceCustom

public class PropertySourceCustom extends ResourcePropertySource {


    public LifeSourcePropertySource(String name, EncodedResource resource) throws IOException {
        super(name, resource);
    }

    public LifeSourcePropertySource(EncodedResource resource) throws IOException {
        super(resource);
    }

    public LifeSourcePropertySource(String name, Resource resource) throws IOException {
        super(name, resource);
    }

    public LifeSourcePropertySource(Resource resource) throws IOException {
        super(resource);
    }

    public LifeSourcePropertySource(String name, String location, ClassLoader classLoader) throws IOException {
        super(name, location, classLoader);
    }

    public LifeSourcePropertySource(String location, ClassLoader classLoader) throws IOException {
        super(location, classLoader);
    }

    public LifeSourcePropertySource(String name, String location) throws IOException {
        super(name, location);
    }

    public LifeSourcePropertySource(String location) throws IOException {
        super(location);
    }

    @Override
    public Object getProperty(String name) {

        if (StringUtils.isNotBlank(System.getenv(name)))
            return System.getenv(name);

        if (StringUtils.isNotBlank(System.getProperty(name)))
            return System.getProperty(name);

        return super.getProperty(name);
    }
}

Quindi, questo mi ha aiutato.


4

Usando il contesto 5.0 di Spring ho ottenuto con successo il caricamento del file di proprietà corretto basato sull'ambiente di sistema tramite la seguente annotazione

@PropertySources({
    @PropertySource("classpath:application.properties"),
    @PropertySource("classpath:application-${MYENV:test}.properties")})

Qui il valore MYENV viene letto dall'ambiente di sistema e se l'ambiente di sistema non è presente, verrà caricato il file delle proprietà dell'ambiente di test predefinito, se si fornisce un valore MYENV errato - l'avvio dell'applicazione non verrà eseguito.

Nota: per ogni profilo, si desidera mantenere - sarà necessario creare un file application- [profile] .property e sebbene abbia usato Spring context 5.0 e non Spring boot - credo che funzionerà anche su Spring 4.1


3

Ho affrontato lo stesso problema dell'autore della domanda. Per il nostro caso, le risposte a questa domanda non sono state sufficienti dal momento che ciascuno dei membri del mio team aveva un ambiente locale diverso e sicuramente avevamo bisogno .gitignoredel file con le diverse stringhe e credenziali di connessione db, quindi le persone non eseguono il commit del file comune per errore e rompere le connessioni db degli altri.

Inoltre, quando abbiamo seguito la procedura di seguito, è stato facile implementare in diversi ambienti e, come ulteriore vantaggio , non abbiamo avuto bisogno di avere alcuna informazione sensibile nel controllo della versione .

Prendendo l'idea dal framework PHP Symfony 3 che ha un parameters.yml(.gitignored) e un parameters.yml.dist(che è un esempio che crea il primo attraverso composer install),

Ho fatto quanto segue combinando le conoscenze dalle risposte seguenti: https://stackoverflow.com/a/35534970/986160 e https://stackoverflow.com/a/35535138/986160 .

In sostanza, ciò offre la libertà di utilizzare l' ereditarietà delle configurazioni a molla e di scegliere i profili attivi attraverso la configurazione nella parte superiore più eventuali credenziali extra come segue:

application.yml.dist (esempio)

    spring:
      profiles:
        active: local/dev/prod
      datasource:
        username:
        password:
        url: jdbc:mysql://localhost:3306/db?useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

application.yml (.gitignore-d sul server dev)

spring:
  profiles:
    active: dev
  datasource:
    username: root
    password: verysecretpassword
    url: jdbc:mysql://localhost:3306/real_db?useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

application.yml (.gitignore-d sul computer locale)

spring:
  profiles:
    active: dev
  datasource:
    username: root
    password: rootroot
    url: jdbc:mysql://localhost:3306/xampp_db?useSSL=false&useLegacyDatetimeCode=false&serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8

application-dev.yml (proprietà specifiche dell'ambiente extra non sensibili)

spring:
  datasource:
    testWhileIdle: true
    validationQuery: SELECT 1
  jpa:
    show-sql: true
    format-sql: true
    hibernate:
      ddl-auto: create-droop
      naming-strategy: org.hibernate.cfg.ImprovedNamingStrategy
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL57InnoDBDialect

Lo stesso si può fare con .properties


0

Se i file delle proprietà sono esternalizzati come variabili di ambiente dopo la configurazione della corsa, è possibile aggiungere in IDE:

--spring.config.additional-location={PATH_OF_EXTERNAL_PROP}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.