Verwenden der env-Variablen in den Application.properties von Spring Boot

198

Wir arbeiten an einer Spring Boot- Webanwendung und die von uns verwendete Datenbank ist MySql .

  • Das Setup, das wir haben, ist, dass wir es zuerst lokal testen (bedeutet, dass wir MySql auf unserem PC installieren müssen);

  • dann drängen wir zu Bitbucket ;

  • Jenkins erkennt den neuen Push an Bitbucket automatisch und baut darauf auf (damit der Jenkins-MVN-Build erfolgreich ist, muss MySql auch auf den virtuellen Maschinen installiert werden, auf denen Jenkins ausgeführt wird).

  • Wenn Jenkins Builds erfolgreich sind , senden wir den Code unter OpenShift an unsere Anwendung (mithilfe des Openshift-Bereitstellungs-Plugins unter Jenkins).

Das Problem, das wir haben, wie Sie vielleicht bereits herausgefunden haben, ist das:

  • In können application.propertieswir die MySQL-Informationen nicht hart codieren. Da unser Projekt an drei verschiedenen Orten ausgeführt wird ( lokal , Jenkins und OpenShift ), müssen wir das Datenquellenfeld dynamisch gestalten application.properties(wir wissen, dass es verschiedene Möglichkeiten gibt, dies zu tun, aber wir arbeiten derzeit an dieser Lösung).

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

Die Lösung, die wir gefunden haben, besteht darin , Systemumgebungsvariablen lokal und in der Jenkins-VM zu erstellen (sie so zu benennen, wie OpenShift sie benennt) und ihnen jeweils die richtigen Werte zuzuweisen:

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"

Wir haben das getan und es funktioniert. Wir haben auch überprüft, Map<String, String> env = System.getenv();ob die Umgebungsvariablen als solche in Java-Variablen umgewandelt werden können:

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");

Jetzt müssen wir nur noch diese Java-Variablen in unserem verwenden, application.propertiesund damit haben wir Probleme.

In welchem Ordner und wie tun müssen wir die zuweisen password, userName, sqlURL, und sqlPortVariablen für application.propertiesLage sein , sie zu sehen , und wie sind wir sie in application.properties?

Wir haben viele Dinge ausprobiert, darunter:

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

Bisher kein Glück. Wir legen diese env-Variablen wahrscheinlich nicht in der richtigen Klasse / im richtigen Ordner ab oder verwenden sie falsch in application.properties.

Ihre Hilfe wird sehr geschätzt!!

Vielen Dank!

SM
quelle
3
Lesen Sie @ConfigurationProperties , um mehr zu erfahren. Dies ist jedoch ein perfekter Anwendungsfall für profilspezifische Konfigurationseigenschaften
Eddie B

Antworten:

266

Sie müssen keine Java-Variablen verwenden. Fügen Sie Ihrer application.propertiesDatei Folgendes hinzu, um System-Env-Variablen einzuschließen :

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}

Der von @Stefan Isele vorgeschlagene Weg ist jedoch vorzuziehen, da Sie in diesem Fall nur eine env-Variable deklarieren müssen : spring.profiles.active. Spring liest die entsprechende Eigenschaftendatei automatisch anhand der application-{profile-name}.propertiesVorlage.

Ken Bekov
quelle
12
Diese Methode ist für die Docker-Verknüpfung bequemer. Zum Beispiel: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
Dies ist absolut der beste Weg. Die Verwendung von Umgebungsvariablen bedeutet, dass Sie neben Ihrer Anwendung keine Geheimnisse im Klartext auflisten müssen. Dies ist wesentlich sicherer und verringert die Abhängigkeit von Ihren Sicherheitsmaßnahmen für den Quellcodezugriff, um Ihr gesamtes Anwesen zu schützen. Ein versehentlicher SO-Beitrag mit enthaltenen Eigenschaften führt nicht dazu, dass Informationen herauskommen.
kipper_t
51
Ich wollte dem hinzufügen und erwähnen, dass, wenn Sie Spring Boot verwenden (nicht überprüft, ob es ohne Boot funktioniert), jede Eigenschaft über eine Umgebungsvariable automatisch überschrieben werden kann, ohne Ihre application.properties zu ändern. Wenn Sie also eine Eigenschaft namens haben, spring.activemq.broker-urllautet die entsprechende Umgebungsvariable : SPRING_ACTIVEMQ_BROKER_URL. Punkte und Striche werden automatisch in Unterstriche umgewandelt. Dies ist äußerst praktisch bei der Arbeit mit Behältern / Federmanschetten.
Ab
15
Wenn Sie für die Cloud entwerfen, ist es nicht vorzuziehen, Spring-Profile zu verwenden. Die Verwendung von Umgebungsvariablen wird vom 12-Faktor-App-Standard empfohlen: 12factor.net/config
Mikhail Golubtsov
6
Ich weiß, dass dieses Thema etwas alt ist. Sie können jedoch sowohl die Einrichtung der Umgebungsvariablen als auch die Einrichtung des Federprofils kombinieren. Ihr Entwicklungsprofil sollte statische Informationen enthalten, während Ihr Produktionsprofil die Umgebungsvariablen verwenden kann. Auf diese Weise müssen Entwickler keine Umgebungsvariablen mehr auf ihrem Computer definieren, wenn sie nur das Entwicklungsprofil bereitstellen möchten.
underscore_05
72

Der einfachste Weg, unterschiedliche Konfigurationen für unterschiedliche Umgebungen zu haben, ist die Verwendung von Federprofilen. Siehe externalisierte Konfiguration .

Dies gibt Ihnen viel Flexibilität. Ich benutze es in meinen Projekten und es ist äußerst hilfreich. In Ihrem Fall hätten Sie 3 Profile: 'local', 'jenkins' und 'openshift'

Sie haben dann 3 Profil spezifische Eigenschaftsdateien: application-local.properties, application-jenkins.propertiesundapplication-openshift.properties

Dort können Sie die Eigenschaften für die betreffende Umgebung festlegen. Wenn Sie die App ausführen, müssen Sie das zu aktivierende Profil wie folgt angeben: -Dspring.profiles.active=jenkins

Bearbeiten

Laut Spring Doc können Sie die Systemumgebungsvariable so einstellen SPRING_PROFILES_ACTIVE, dass Profile aktiviert werden, ohne sie als Parameter übergeben zu müssen.

Gibt es eine Möglichkeit, die aktive Profiloption für die Web-App zur Laufzeit zu übergeben?

Nein. Spring bestimmt die aktiven Profile als einen der ersten Schritte beim Erstellen des Anwendungskontexts. Die aktiven Profile werden dann verwendet, um zu entscheiden, welche Eigenschaftendateien gelesen und welche Beans instanziiert werden. Sobald die Anwendung gestartet ist, kann dies nicht mehr geändert werden.

Stefan Isele - prefabware.com
quelle
4
Ich mag diese Antwort, aber was ist, wenn der Profilname aus der Umgebung stammen soll? Ich habe versucht -Dspring.active.profiles = $ SPRING_ACTIVE_PROFILES und das Betriebssystem env var in /etc/profile.d/myenvvars.sh festgelegt, aber Spring Boot nimmt das nicht auf
Tom Hartwell
1
SPRING_PROFILES_ACTIVE funktioniert aufgrund der entspannten Bindungsfunktion von Spring Boot docs.spring.io/spring-boot/docs/1.3.0.BUILD-SNAPSHOT/reference/…
feed.me
5
danke für diese Antwort Stefan, es hat bei mir funktioniert, aber mit einer Änderung - die Eigenschaft ist eigentlich spring.profiles.active und nicht spring.active.profiles
Rudi
10
Federprofile können zwar sehr nützlich sein, sind jedoch in Bezug auf das OP nicht geeignet. Dies liegt daran, wie der Quellcode gespeichert wird und wie empfindlich die damit gespeicherten Eigenschaftsinformationen sind. Der OP-Kontext besteht aus dem Datenbankzugriff. In dieser Situation möchten Sie keine Produktdetails im Klartext in der Quelle. Dies bedeutet, wenn die Quelle gefährdet ist, ist auch die Datenbank gefährdet. Es ist besser, dafür env-Variablen oder geheime Tools wie Vault zu verwenden. Ich bevorzuge env. Ich würde auch dafür sorgen, dass alle Umgebungen in dieser Hinsicht aus Gründen der Konsistenz gleich funktionieren. Es vermeidet Unfälle in der Zukunft.
kipper_t
2
Sie können eine Spring Boot-Profileigenschaftendatei außerhalb der Anwendungs-JAR verwenden. Diese umgebungsspezifische Datei wird beispielsweise auf application-production.propertiessichere Weise auf der Produktionsmaschine bereitgestellt und befindet sich normalerweise nicht im Quellcode-Repository der Anwendung.
Colin D Bennett
13

Dies ist eine Reaktion auf eine Reihe von Kommentaren, da mein Ruf nicht hoch genug ist, um direkt zu kommentieren.

Sie können das Profil zur Laufzeit angeben, solange der Anwendungskontext noch nicht geladen wurde.

// 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");
gthazmatt
quelle
12

Flayway erkennt die direkten Umgebungsvariablen in den application.properties (Spring-Boot V2.1) nicht. z.B

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

Um dieses Problem zu lösen, habe ich diese Umgebungsvariablen erstellt. Normalerweise erstelle ich die Datei .env:

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

Und exportiere die Variablen in meine Umgebung:

export $(cat .env | xargs)

Und zum Schluss führen Sie einfach den Befehl aus

mvn spring-boot:run

Oder führen Sie Ihre JAR-Datei aus

java -jar target/your-file.jar

Hier gibt es einen anderen Ansatz: https://docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/maven-plugin/examples/run-env-variables.html

Felipe Girotti
quelle
1
Was ist env-vars? Wie werden sie verwendet? Ihre Antwort bezieht sich auf Dinge ohne vollständige Beschreibung und Sie fügen keine Links hinzu. Ich habe dies fast abgelehnt, aber ich sehe, dass Ihr Repräsentant 21 Jahre alt ist, also sind Sie neu und eine Person fand Ihre Antwort nützlich. Ich lasse sie los, versuche aber, in zukünftigen Antworten weitere Informationen bereitzustellen, und begrüße SO (Stack Overflow). Ich hoffe es gefällt euch genauso gut wie mir.
PatS
2
Danke @PatS, ich habe weitere Details hinzugefügt, hoffe es wird nützlich sein.
Felipe Girotti
1
Hervorragende Änderungen. Vielen Dank, dass Sie Ihre Antwort aktualisiert haben.
PatS
9

Hier ist ein Snippet-Code durch eine Kette von Umgebungseigenschaften, die für verschiedene Umgebungen geladen werden.

Eigenschaftendatei unter Ihren Anwendungsressourcen ( src / main / resources ): -

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

Im Idealfall enthält application.properties alle allgemeinen Eigenschaften, auf die für alle Umgebungen zugegriffen werden kann, und umgebungsbezogene Eigenschaften funktionieren nur in bestimmten Umgebungen. Daher ist die Reihenfolge des Ladens dieser Eigenschaftendateien wie folgt:

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

Code-Snippet hier: -

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

        }
    }
Ajay Kumar
quelle
2
Behandelt Spring Boot dieses Szenario nicht sofort? Siehe Dokumentation zur externen Konfiguration hier
ChickenFeet
4

Vielleicht schreibe ich das zu spät, aber ich habe das ähnliche Problem bekommen, als ich versucht habe, Methoden zum Lesen von Eigenschaften zu überschreiben.

Mein Problem war: 1) Eigenschaft aus env lesen, wenn diese Eigenschaft in env festgelegt wurde 2) Eigenschaft aus Systemeigenschaft lesen, wenn diese Eigenschaft in Systemeigenschaft festgelegt wurde 3) Und zuletzt aus Anwendungseigenschaften lesen.

Um dieses Problem zu beheben, gehe ich zu meiner Bean-Konfigurationsklasse

@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);
    }
}

Und Fabrik in @PropertySource überschreiben. Und dann habe ich meine eigene Implementierung zum Lesen von Eigenschaften erstellt.

    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);
        }


    }

Und PropertySourceCustom erstellt

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);
    }
}

Das hat mir also geholfen.

Maksym Galich
quelle
4

Mit Spring Context 5.0 habe ich erfolgreich das Laden der richtigen Eigenschaftendatei basierend auf der Systemumgebung über die folgende Anmerkung erreicht

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

Hier wird der MYENV-Wert aus der Systemumgebung gelesen. Wenn keine Systemumgebung vorhanden ist, wird die Standard-Eigenschaftsdatei der Testumgebung geladen. Wenn ich einen falschen MYENV-Wert gebe, kann die Anwendung nicht gestartet werden.

Hinweis: Für jedes Profil, das Sie pflegen möchten, müssen Sie eine Anwendungs- [Profil] .property-Datei erstellen. Obwohl ich Spring Context 5.0 und nicht Spring Boot verwendet habe, funktioniert dies meines Erachtens auch unter Spring 4.1

Abdeali Chandanwala
quelle
3

Ich hatte das gleiche Problem wie der Autor der Frage. Für unseren Fall reichten die Antworten in dieser Frage nicht aus, da jedes Mitglied meines Teams eine andere lokale Umgebung hatte und wir definitiv .gitignoredie Datei mit der unterschiedlichen Datenbankverbindungszeichenfolge und den Anmeldeinformationen benötigen, damit die Benutzer die gemeinsame Datei nicht festschreiben versehentlich und brechen Sie die Datenbankverbindungen anderer.

Darüber hinaus war die Bereitstellung in verschiedenen Umgebungen einfach, und als zusätzlichen Bonus mussten wir überhaupt keine vertraulichen Informationen in der Versionskontrolle haben .

Die Idee aus dem PHP Symfony 3-Framework mit einem parameters.yml(.gitignored) und einem parameters.yml.dist(das ist ein Beispiel, das das erste durch erstellt) erhaltencomposer install ) erhalten,

Ich habe das Wissen aus den folgenden Antworten wie folgt kombiniert: https://stackoverflow.com/a/35534970/986160 und https://stackoverflow.com/a/35535138/986160 .

Dies gibt im Wesentlichen die Freiheit, die Vererbung von Federkonfigurationen zu verwenden und aktive Profile durch Konfiguration oben zu wählen, sowie alle besonders vertraulichen Anmeldeinformationen wie folgt:

application.yml.dist (Beispiel)

    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 auf dem Dev-Server)

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 auf lokalem Computer)

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 (zusätzliche umgebungsspezifische Eigenschaften nicht empfindlich)

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

Gleiches kann mit .properties gemacht werden

Michail Michailidis
quelle