Подтвердить что ты не робот

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

Мы работаем над веб-приложением 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 в нужный класс/папку или неправильно используем их в application.properties.

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

Спасибо!

4b9b3361

Ответ 1

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

Ответ 2

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

Это дает вам большую гибкость. Я использую его в своих проектах, и это очень полезно. В вашем случае у вас будет 3 профиля: "local", "jenkins" и "openshift"

Затем у вас есть 3 файла свойств профиля: application-local.properties, application-jenkins.properties и application-openshift.properties

Там вы можете установить свойства для соответствующей среды. Когда вы запускаете приложение, вы должны указать профиль для активации следующим образом: -Dspring.profiles.active=jenkins

редактировать

Согласно весеннему документу вы можете установить системную переменную среды SPRING_PROFILES_ACTIVE чтобы активировать профили и не нужно передавать ее в качестве параметра.

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

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

Ответ 3

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

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

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

Ответ 4

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

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

        }
    }

Ответ 5

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.

Ответ 6

Использование 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

Ответ 7

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

Моя проблема: 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);
    }
}

И перезапишите factory в @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);
    }
}

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

Ответ 8

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

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

Извлечение идеи из среды PHP Symfony 3, в которой есть parameters.yml (.gitignored) и parameters.yml.dist (пример, который создает первый файл при composer install),

Я сделал следующее, комбинируя знания из ответов ниже: fooobar.com/info/128128/... и fooobar.com/info/128128/....

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

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