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

时间:2016-02-21 02:39:19

标签: java mysql spring spring-mvc openshift

我们正在开发 Spring Boot 网络应用,我们使用的数据库是 MySql ;

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

  • 然后我们推送到 Bitbucket ;

  • Jenkins 会自动检测到对Bitbucket的新推送并对其进行构建(对于Jenkins mvn build,我们还需要在运行Jenkins的虚拟机上安装MySql)。

  • 如果Jenkins构建了传递,我们将代码推送到 OpenShift 上的应用程序(使用Jenkins上的Openshift部署插件)。

我们遇到的问题 正如您可能已经发现的那样:

  • application.properties中,我们无法对MySql信息进行硬编码。由于我们的项目将在3个不同的地方(本地 Jenkins OpenShift )运行,我们需要在{{中使数据源字段动态化1}}(我们知道有不同的做法,但我们现在正在研究这个解决方案。)

    application.properties

我们提出的解决方案是我们在本地和Jenkins vm中创建系统环境变量(以与OpenShift命名的方式命名它们)并分别为它们分配正确的值:

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

我们已经完成了这项工作。我们还使用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" 检查了环境变量是否可以变成java变量:

Map<String, String> env = System.getenv();

现在唯一剩下的就是我们需要在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分配passworduserNamesqlURLsqlPort变量看到它们以及我们如何将它们包含在application.properties

我们尝试了很多其中一件事:

application.properties

到目前为止没有运气。我们可能没有将这些env变量放在正确的类/文件夹中,或者在spring.datasource.url = ${sqlURL}:${sqlPort}/"nameofDB" spring.datasource.username = ${userName} spring.datasource.password = ${password} 中错误地使用它们。

非常感谢您的帮助!!

谢谢!

9 个答案:

答案 0 :(得分:129)

您不需要使用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模板自动读取相应的属性文件。

答案 1 :(得分:40)

对不同环境进行不同配置的最简单方法是使用弹簧配置文件。请参阅externalised configuration

这为您提供了很大的灵活性,我在我的项目中使用它,它非常有用。 在您的情况下,您将有3个配置文件 &#39;本地&#39; jenkins&#39;和&#39; openshift&#39;

然后,您有3个配置文件特定的属性文件 lbDeliveryDate.Text = dr["DeliveryDate"].ToString(); DateTime dt = Convert.ToDateTime(dr["DeliveryDate"].ToString()); lbDeliveryDate.Text = String.format("{0}/{1}/{2}",dt.Month ,dt.Day, dt.Year ); application-local.properties

您可以在此处设置相关环境的属性。 当您运行应用程序时,您必须指定要激活的配置文件 application-jenkins.properties

修改

根据spring doc,您只需设置系统环境变量application-openshift.properties即可激活配置文件,不要将其作为参数传递。

有没有办法在运行时为Web应用程序传递活动配置文件选项?

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

答案 2 :(得分:7)

这是对一些评论的回应,因为我的声誉不足以直接评论。

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

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

答案 3 :(得分:5)

Here是通过一系列环境的代码段代码,为不同的环境加载属性文件。

应用程序资源下的属性文件( 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);

        }
    }

答案 4 :(得分:3)

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

答案 5 :(得分:2)

使用Spring context 5.0我已成功通过以下注释

基于系统环境加载了正确的属性文件
@PropertySources({
    @PropertySource("classpath:application.properties"),
    @PropertySource("classpath:application-${MYENV:test}.properties")})

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

注意:对于每个配置文件,您需要维护 - 您需要创建一个application- [profile] .property文件,尽管我使用了Spring context 5.0 &amp;不是Spring启动 - 我相信这也适用于Spring 4.1

答案 6 :(得分:1)

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

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

所以,这对我有所帮助。

答案 7 :(得分:1)

我遇到了与问题作者相同的问题。对于我们的案例,此问题的答案还不够,因为我们团队中的每个成员都具有不同的本地环境,我们肯定需要.gitignore具有不同数据库连接字符串和凭据的文件,所以人们不会t错误地提交公用文件并中断其他人的数据库连接。

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

从PHP Symfony 3框架(具有parameters.yml(。gitignored)和parameters.yml.dist(这是创建到composer install的第一个示例)中获取想法,

我结合了以下答案中的知识,进行了以下工作:https://stackoverflow.com/a/35534970/986160https://stackoverflow.com/a/35535138/986160

从本质上讲,这使您可以自由使用inheritance of spring configurations并通过顶部的配置以及以下任何其他敏感凭据来选择活动配置文件:

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: 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来完成

答案 8 :(得分:1)

如果将运行配置之后的属性文件外部化为环境变量,则可以将其添加到IDE中:

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