Использование переменной 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
.
Ваша помощь высоко ценится!
Спасибо!
Ответы
Ответ 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