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


197

我们正在开发一个Spring Boot Web应用程序,并且正在使用的数据库是MySql

  • 我们拥有的设置是我们首先在本地对其进行测试(意味着我们需要在PC上安装MySql);

  • 然后我们推向Bitbucket ;

  • Jenkins自动检测对Bitbucket的新推送并在其上进行构建(要使Jenkins mvn构建通过,我们还需要在运行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");

现在剩下的唯一事情就是我们需要在我们的程序中使用这些java变量application.properties,这就是我们遇到的麻烦。

在哪个文件夹,以及如何做,我们需要分配passworduserNamesqlURL,和sqlPort变量application.properties能够看到他们,我们如何将它们包含在application.properties

我们尝试了许多事情,其中​​之一是:

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

到目前为止没有运气。我们可能没有将这些env变量放在正确的类/文件夹中,或者在中错误地使用了它们application.properties

非常感谢您的帮助!

谢谢!


3
阅读@ConfigurationProperties以了解更多信息。但是,这是Profile特定配置属性
Eddie B

Answers:


264

您不需要使用Java变量。要包括系统环境变量,请在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模板自动读取适当的属性文件。


12
此方法对于docker链接更方便。例如: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
的Firat KUCUK

16
这绝对是最好的方法。使用环境变量意味着您无需在应用程序旁边以纯文本形式列出机密信息。这样可以显着提高安全性,并减少对源代码访问安全性措施的依赖,从而保护您的整个财产。带有属性的意外SO帖子不会导致信息泄漏。
kipper_t

51
我想补充一点,并提到如果您使用的是Spring Boot(不检查它是否可以在没有Boot的情况下使用),那么可以通过环境变量自动覆盖任何属性,而无需修改application.properties。也就是说,如果您有一个名为的属性,spring.activemq.broker-url则相应的环境变量将是:SPRING_ACTIVEMQ_BROKER_URL。句号和破折号会自动转换为下划线。使用容器/弹簧靴时,这非常方便。
安倍晋三

14
如果您为云设计,那不是使用Spring配置文件的首选方法。12因子应用程序标准建议使用环境变量:12factor.net/config
Mikhail Golubtsov

6
我知道这个话题有点老了。但是您可以将环境变量设置和弹簧轮廓设置结合在一起。开发配置文件应该具有静态信息,而生产配置文件可以使用环境变量。这样,如果开发人员只想部署开发配置文件,则不再需要在其计算机上定义环境变量。
underscore_05

72

对于不同的环境具有不同配置的最简单方法是使用弹簧轮廓。请参阅外部化配置

这给您很大的灵活性。我在项目中使用它,它非常有帮助。在您的情况下,您将拥有3个配置文件:“本地”,“詹金斯”和“ openshift”

然后,您有3个特定于配置文件的属性文件: application-local.propertiesapplication-jenkins.properties,和application-openshift.properties

您可以在此处设置相关环境的属性。运行该应用程序时,您必须指定要激活的配置文件,如下所示: -Dspring.profiles.active=jenkins

编辑

根据spring doc可以设置系统环境变量 SPRING_PROFILES_ACTIVE为激活配置文件,而无需将其作为参数传递。

有什么方法可以在运行时通过Web应用程序的活动配置文件选项?

不会。在构建应用程序上下文时,Spring将活动配置文件确定为第一步之一。然后,使用活动概要文件来决定读取哪些属性文件以及实例化哪些bean。一旦启动应用程序,则无法更改。


4
我喜欢这个答案,但是如果您希望配置文件名称来自环境怎么办?我已经尝试过-Dspring.active.profiles = $ SPRING_ACTIVE_PROFILES,并在/etc/profile.d/myenvvars.sh中设置OS env var,但是Spring Boot并没有做到这一点
Tom Hartwell,

1
SPRING_PROFILES_ACTIVE工作因为春季启动的轻松绑定功能的docs.spring.io/spring-boot/docs/1.3.0.BUILD-SNAPSHOT/reference/...
feed.me

5
感谢您的回答,Stefan对我有用,但是做了一个更改-该属性实际上是spring.profiles.active而不是spring.active.profiles
Rudi

10
尽管Spring概要文件可能非常有用,但相对于OP而言,它们是不合适的。这是由于源代码的存储方式以及随其存储的属性信息的敏感性。OP上下文围绕数据库访问。对于这种情况,您不希望在源代码中以纯文本形式显示产品详细信息。这意味着,如果源受到破坏,那么数据库也将受到破坏。最好为此使用env变量或秘密工具,例如Vault。我更喜欢环境。在一致性方面,我还将使所有环境以相同的方式运行。它避免了将来发生事故。
kipper_t

2
您可以使用应用程序JAR外部的Spring Boot配置文件属性文件。例如,此特定于环境的文件application-production.properties将以安全的方式部署到生产计算机,并且通常不在应用程序源代码存储库中。
Colin D Bennett

13

这是对许多评论的回应,因为我的声誉不够高,无法直接发表评论。

只要尚未加载应用程序上下文,就可以在运行时指定配置文件。

// 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无法将直接环境变量识别到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


1
什么是env-vars?他们如何使用。您的答案是指没有完整描述的内容,并且不包含任何链接。我几乎对此表示反对,但我发现您的代表是21岁,所以您是新手,一个人发现您的答案有用,所以我放手,但尝试在将来的答案中提供更多信息,并欢迎您使用SO(堆栈溢出)。希望您能像我一样喜欢它。
PatS

2
感谢@PatS,我添加了更多详细信息,希望它将对您有所帮助。
菲利佩·吉罗蒂

1
很棒的变化。感谢您更新答案。
PATS

9

这里是通过一系列环境属性文件的片段代码,这些文件针对不同的环境进行加载。

应用程序资源(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);

        }
    }

2
Spring Boot不能立即解决这种情况吗?请参阅此处的外部配置文档
ChickenFeet

4

也许我写得太晚了,但是当我尝试覆盖读取属性的方法时,也遇到了类似的问题。

我的问题是: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);
    }
}

因此,这对我有所帮助。


4

使用Spring context 5.0,我通过以下注释成功实现了基于系统环境加载正确的属性文件

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

在这里,从系统环境中读取MYENV值,如果不存在系统环境,则将加载默认的测试环境属性文件,如果我输入了错误的MYENV值,它将无法启动应用程序。

注意:对于每个配置文件,您都需要维护-您将需要创建一个application- [profile] .property文件,尽管我使用的是Spring context 5.0 ,而不是Spring boot-我相信这也可以在Spring 4.1上使用


3

我遇到了与问题作者相同的问题。对于我们的案例,此问题的答案还不够,因为我们团队的每个成员都具有不同的本地环境,我们绝对需要.gitignore具有不同数据库连接字符串和凭据的文件,因此人们不必提交通用文件错误地破坏别人的数据库连接。

最重要的是,当我们按照以下步骤操作时,可以轻松地在不同的环境中进行部署,并且作为额外的好处,我们根本不需要在版本控制中包含任何敏感信息

从PHP Symfony 3框架parameters.yml(具有(.gitignored)和a 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(开发服务器上的.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: 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(特定于环境的其他属性不敏感)

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可以完成相同的操作

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.