Использование переменной 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.properties
application-jenkins.properties
application-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.