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


201

Мы работаем над веб-приложением 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.

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

Спасибо!


3
Прочитайте @ConfigurationProperties, чтобы узнать больше. Тем не менее, это идеальный вариант использования специфичных
— Eddie B

Ответы:


271

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


12
Этот метод более удобен для связи с докером. Например: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
— Fırat KÜÇÜK

17
Это абсолютно лучший путь. Использование переменных среды означает, что вам не нужно перечислять секреты в виде простого текста рядом с вашим приложением. Это значительно более безопасно и снижает зависимость от ваших мер безопасности доступа к исходному коду для защиты всего вашего имущества. Случайный пост SO с включенными свойствами не приводит к утечке информации.
— kipper_t

51
Я хотел бы добавить к этому и упомянуть, что если вы используете весеннюю загрузку (не проверял, работает ли она без загрузки), то любое свойство может быть автоматически переопределено через переменную окружения без изменения вашего application.properties. то есть, если у вас есть свойство , spring.activemq.broker-urlто соответствующая переменная окружения будет: SPRING_ACTIVEMQ_BROKER_URL. точки и тире автоматически преобразуются в подчеркивания. Это чрезвычайно удобно при работе с контейнерами / пружинной загрузкой.
— Эйб

15
Если вы разрабатываете для облака, это не лучший способ использовать профили Spring. Использование переменных среды рекомендуется стандартом 12-факторного приложения: 12factor.net/config
— Михаил Голубцов

6
Я знаю, что эта тема немного старая. Но вы можете комбинировать как настройку переменных среды, так и настройку профиля пружины. Ваш профиль разработчика должен иметь статическую информацию, в то время как ваш производственный профиль может использовать переменные среды. Таким образом, разработчикам больше не нужно определять переменные среды на своем компьютере, если они просто хотят развернуть профиль разработки.
— underscore_05

72

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

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

После этого у вас 3 профиля файлы конкретных свойств: application-local.properties, application-jenkins.propertiesиapplication-openshift.properties

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

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

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

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

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


4
Мне нравится этот ответ, но что, если вы хотите, чтобы имя профиля пришло из среды? Я попробовал -Dspring.active.profiles = $ SPRING_ACTIVE_PROFILES и установил переменную env var в /etc/profile.d/myenvvars.sh, но Spring Boot этого не понимает
— Том Хартвелл,

1
SPRING_PROFILES_ACTIVE работает из-за непринужденной привязки весенней загрузки docs.spring.io/spring-boot/docs/1.3.0.BUILD-SNAPSHOT/reference/…
— feed.me

5
спасибо за этот ответ, Стефан, он сработал для меня, но с одним изменением - это свойство на самом деле spring.profiles.active, а не spring.active.profiles
— Руди

11
В то время как пружинные профили могут быть очень полезны, в отношении OP они не подходят. Это связано с тем, как хранится исходный код, и чувствительностью информации о свойствах, хранящейся в нем. Контекст OP связан с доступом к базе данных. Для этой ситуации вам не нужны подробности о продукции в виде простого текста в источнике. Это означает, что если источник скомпрометирован, то база данных также скомпрометирована. Для этого лучше использовать переменные env или секретные инструменты, такие как Vault. Я предпочитаю env. Я также заставил бы все среды работать одинаково в отношении согласованности. Это позволяет избежать несчастных случаев в будущем.
— kipper_t

2
Вы можете использовать файл свойств профиля Spring Boot, внешний по отношению к JAR приложения. Этот специфичный для среды файл, например, application-production.propertiesбудет развернут на производственном компьютере безопасным способом и обычно не будет находиться в хранилище исходного кода приложения.
— Колин Д. Беннетт

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 не обрабатывает этот сценарий из коробки? Смотрите документацию по External Config здесь
— ChickenFeet

4

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

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

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


4

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


3

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

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

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

Я сделал следующее, объединив знания из ответов ниже: https://stackoverflow.com/a/35534970/986160 и https://stackoverflow.com/a/35535138/986160 .

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

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


0

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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.