簡體   English   中英

在 Spring Boot 的 application.properties 中使用 env 變量

[英]Using env variable in Spring Boot's application.properties

我們正在開發一個 Spring Boot Web 應用程序,我們使用的數據庫是 MySQL;

  • 我們的設置是我們首先在本地測試它(意味着我們需要在我們的 PC 上安裝 MySQL);

  • 然后我們推送到 Bitbucket;

  • Jenkins 會自動檢測到 Bitbucket 的新推送並在其上進行構建(為了讓 Jenkins mvn build 通過,我們還需要在運行 Jenkins 的虛擬機上安裝 MySQL)。

  • 如果 Jenkins 構建通過,我們將代碼推送到 OpenShift 上的應用程序(使用 Jenkins 上的 Openshift 部署插件)。

正如您可能已經發現的那樣,我們遇到的問題是:

  • application.properties我們不能硬編碼 MySQL 信息。 由於我們的項目將在 3 個不同的地方運行( localJenkinsOpenShift ),我們需要在application.properties中使數據源字段動態化(我們知道有不同的方法,但我們現在正在研究這個解決方案)。

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

我們提出的解決方案是我們在本地和 Jenkins VM 中創建系統環境變量(以與 OpenShift 命名它們相同的方式命名它們),並分別為它們分配正確的值:

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"

我們已經做到了這一點,它的工作原理。 我們還檢查了Map<String, String> env = System.getenv(); 環境變量可以這樣變成java變量:

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

現在唯一剩下的就是我們需要在application.properties使用這些 java 變量,這就是我們遇到的麻煩。

我們需要在哪個文件夾中以及如何為application.properties分配passworduserNamesqlURLsqlPort變量以便能夠看到它們,以及我們如何將它們包含在application.properties

我們嘗試了很多事情,其中​​之一是:

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

到目前為止沒有運氣。 我們可能沒有將這些環境變量放在正確的類/文件夾中,或者在application.properties錯誤地使用它們。

您不需要使用java變量。 要包含系統env變量,請將以下內容添加到application.properties文件中:

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}

但是@Stefan Isele建議的方式更可取,因為在這種情況下你只需要聲明一個env變量: spring.profiles.active Spring將通過application-{profile-name}.properties模板自動讀取相應的屬性文件。

對不同環境進行不同配置的最簡單方法是使用彈簧輪廓。 請參閱外部化配置

這為您提供了很大的靈活性。 我在我的項目中使用它,它非常有用。 在你的情況下,你將有3個配置文件:'local','jenkins'和'openshift'

然后,您有3個配置文件特定的屬性文件: application-local.propertiesapplication-jenkins.propertiesapplication-openshift.properties

在那里,您可以設置相關環境的屬性。 當您運行應用程序時,您必須指定要激活的配置文件,如下所示: -Dspring.profiles.active=jenkins

編輯

根據spring doc,您可以設置系統環境變量SPRING_PROFILES_ACTIVE來激活配置文件,而不需要將其作為參數傳遞。

有沒有辦法在運行時為Web應用程序傳遞活動配置文件選項?

在構建應用程序上下文時,Spring將活動配置文件確定為第一步。 然后使用活動配置文件來確定讀取哪些屬性文件以及實例化哪些bean。 應用程序啟動后,無法更改。

這是對一些評論的回應,因為我的聲譽不足以直接評論。

只要尚未加載應用程序上下文,您就可以在運行時指定配置文件。

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

Flayway無法將直接環境變量識別到application.properties (Spring-Boot V2.1)中。 例如

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

為了解決這個問題,我做了這個環境變量,通常我創建文件.env:

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

並將變量導出到我的環境:

export $(cat .env | xargs)

最后只需運行命令

mvn spring-boot:run

或者運行你的jar文件

java -jar target/your-file.jar

還有另一種方法: https//docs.spring.io/spring-boot/docs/2.1.0.BUILD-SNAPSHOT/maven-plugin/examples/run-env-variables.html

是通過一系列環境的代碼片段代碼,為不同的環境加載屬性文件。

應用程序資源下的屬性文件( src / main / resources ): -

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

理想情況下, application.properties包含可供所有環境訪問的所有公共屬性,而環境相關屬性僅適用於指定環境。 因此加載這些屬性文件的順序將是這樣的 -

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

這里的代碼片段: -

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

        }
    }

也許我寫得太晚了,但是當我試圖覆蓋讀取屬性的方法時,我遇到了類似的問題。

我的問題是:1)如果在env中設置了此屬性,則從env讀取屬性2)如果在系統屬性中設置了此屬性,則從系統屬性讀取屬性3)最后,從應用程序屬性中讀取。

所以,為了解決這個問題,我去了我的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);
    }
}

並在@PropertySource中覆蓋工廠。 然后我創建了自己的讀取屬性的實現。

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


    }

並創建了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);
    }
}

所以,這對我有所幫助。

使用Spring context 5.0我已經通過以下注釋成功地基於系統環境加載了正確的屬性文件

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

這里從系統環境讀取MYENV值,如果系統環境不存在,則將加載默認測試環境屬性文件,如果我給出錯誤的MYENV值 - 它將無法啟動應用程序。

注意:對於每個配置文件,您需要維護 - 您需要創建一個application- [profile] .property文件,雖然我使用Spring上下文5.0 而不是Spring引導 - 我相信這也適用於Spring 4.1

如果屬性文件在運行配置之后被外部化為環境變量,則可以將其添加到 IDE 中:

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

我遇到了與問題的作者相同的問題。 對於我們的案例,這個問題的答案還不夠,因為我團隊的每個成員都有不同的本地環境,我們肯定需要.gitignore具有不同數據庫連接字符串和憑據的文件,所以人們不提交錯誤的常見文件並打破其他人的數據庫連接。

最重要的是,當我們按照下面的步驟操作時,它很容易在不同的環境中部署,並且作為額外的獎勵我們根本不需要在版本控制中包含任何敏感信息

從PHP Symfony 3框架獲取想法,該框架具有parameters.yml (.gitignored)和parameters.yml.dist (這是通過composer install創建第一個的示例),

我將以下答案中的知識結合起來: https//stackoverflow.com/a/35534970/986160https://stackoverflow.com/a/35535138/986160

從本質上講,這可以自由地使用spring配置的繼承,並通過頂部配置和任何額外的敏感憑證選擇活動配置文件,如下所示:

application.yml.dist(樣本)

    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(dev服務器上的.gitignore-d)

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)

spring:
  profiles:
    active: local
  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(額外的環境特定屬性不敏感)

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

可以使用.properties完成相同的操作

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM