Использование переменной env в application.properties Spring Boot

Мы работаем над веб-приложением Spring Boot, и база данных, которую мы используем, - MySql;

  • у нас есть настройки, которые мы сначала тестируем локально (это означает, что нам нужно установить MySql на наш ПК);

  • затем мы нажимаем на Bitbucket;

  • Jenkins автоматически обнаруживает новый push-запрос к Bitbucket и выполняет его сборку (для того, чтобы передать Jenkins mvn build, нам также нужно установить MySql на виртуальных машинах, на которых работает Jenkins).

  • если сборка Jenkins проходит успешно, мы отправляем код в наше приложение в OpenShift (используя плагин развертывания Openshift в Jenkins).

Проблема, с которой мы столкнулись, как вы, возможно, уже поняли, заключается в следующем:

  • в application.properties мы не можем жестко закодировать информацию MySql. Поскольку наш проект будет выполняться в 3 разных местах (локальном, Jenkins и OpenShift), нам нужно сделать поле источника данных динамичным в 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 и с этим у нас проблемы.

В какой папке и как нам нужно назначить password, userName, sqlURL, а также sqlPort переменные для application.properties чтобы иметь возможность видеть их и как мы включаем их в application.properties?

Мы перепробовали много вещей, одна из которых:

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

Пока не повезло. Мы, вероятно, не помещаем эти переменные env в правильный класс / папку и используем их правильно в applicatin.properties,

Ваша помощь высоко ценится!

Спасибо!

10 ответов

Вам не нужно использовать переменные 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_PORT}

Но способ, предложенный @Stefan Isele, более предпочтителен, потому что в этом случае вы должны объявить только один env vaireable: 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_PROFILES_ACTIVE, чтобы активировать профили и не передавать их в качестве параметра.

Есть ли способ передать параметр активного профиля для веб-приложения во время выполнения?

Нет. Spring определяет активные профили как один из первых шагов при создании контекста приложения. Активные профили затем используются, чтобы решить, какие файлы свойств будут прочитаны и какие компоненты будут созданы. После запуска приложения это нельзя изменить.

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

Это в ответ на ряд комментариев, так как моя репутация недостаточно высока, чтобы комментировать напрямую.

Вы можете указать профиль во время выполнения, если контекст приложения еще не загружен.

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

Вот фрагмент кода через цепочку файлов свойств среды, загружаемых для разных сред.

Файл свойств под ресурсами вашего приложения (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);

        }
    }

Я столкнулся с той же проблемой, что и автор вопроса. Для нашего случая ответов на этот вопрос было недостаточно, так как каждый из членов моей команды имел различную местную среду, и нам определенно нужно было .gitignore файл, который имел другую строку подключения к базе данных и учетные данные, поэтому люди не фиксируют общий файл по ошибке и не разрывают подключения к базе данных других пользователей.

Вдобавок ко всему, когда мы следовали приведенной ниже процедуре, ее было легко развернуть в разных средах, и в качестве дополнительного бонуса нам не нужно было вообще иметь какую-либо конфиденциальную информацию в управлении версиями.

Получение идеи из среды PHP Symfony 3, которая имеет parameters.yml (.gitignored) и parameters.yml.dist (это образец, который создает первый через composer install),

Я сделал следующее, объединяя знания из ответов ниже: /questions/48097173/ispolzovanie-peremennoj-env-v-applicationproperties-spring-boot/48097283#48097283 и /questions/48097173/ispolzovanie-peremennoj-env-v-applicationproperties-spring-boot/48097297#48097297.

По сути, это дает свободу использовать наследование конфигураций пружин и выбирать активные профили через конфигурацию в верхней части, а также любые дополнительные конфиденциальные учетные данные следующим образом:

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

Используя 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

Может быть, я пишу это слишком поздно, но у меня возникла аналогичная проблема, когда я пытался переопределить методы для чтения свойств.

Моя проблема была: 1) Чтение свойства из env, если это свойство было установлено в env 2) Чтение свойства из системного свойства, если это свойство было установлено в системном свойстве 3) И, наконец, чтение из свойств приложения.

Итак, для решения этой проблемы я иду в свой класс конфигурации бина

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

Итак, это помогло мне.

Если файлы свойств вынесены во внешний вид как переменные среды, в IDE можно добавить следующую конфигурацию запуска:

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

Просто добавьте зависимостьspring-dotenvв ваш build.gradle или pom.xml.

      dependencies {
    implementation 'me.paulschwarz:spring-dotenv:4.0.0'
}
      <dependency>
    <groupId>me.paulschwarz</groupId>
    <artifactId>spring-dotenv</artifactId>
    <version>4.0.0</version>
</dependency>
  • Затем просто напишите секреты в .env как пару ключ-значение. И используйте их, как показано ниже.
      spring:
  data:
    mongodb:
      uri: ${URL}
      username: ${USERNAME}
      password: ${PASSWORD}
      database: ${DATABASE}

Если версия старше, напишите как

          spring:
      data:
          mongodb:
              uri: ${env.URL}
              username: ${env.USERNAME}
              password: ${env.PASSWORD}
              database: ${env.DATABASE}

Ознакомьтесь с полным использованием или проектом на GitHub.

Другие вопросы по тегам