Перейти к основному содержимому

Свойства с Spring и Spring Boot

· 9 мин. чтения

Задача: Медиана двух отсортированных массивов

Даны два отсортированных массива размерами n и m. Найдите медиану слияния этих двух массивов.
Временная сложность решения должна быть O(log(m + n)) ...

ANDROMEDA

1. Обзор

В этом руководстве показано, как настраивать и использовать свойства в Spring с помощью конфигурации Java и @PropertySource.

Мы также увидим, как свойства работают в Spring Boot.

2. Зарегистрируйте файл свойств через аннотации

Spring 3.1 также представляет новую аннотацию @PropertySource как удобный механизм для добавления источников свойств в среду.

Мы можем использовать эту аннотацию в сочетании с аннотацией @Configuration :

@Configuration
@PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
//...
}

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

@PropertySource({ 
"classpath:persistence-${envTarget:mysql}.properties"
})
...

2.1. Определение нескольких местоположений свойств

Аннотация @PropertySource повторяема в соответствии с соглашениями Java 8 . Поэтому, если мы используем Java 8 или выше, мы можем использовать эту аннотацию для определения нескольких местоположений свойств:

@PropertySource("classpath:foo.properties")
@PropertySource("classpath:bar.properties")
public class PropertiesWithJavaConfig {
//...
}

Конечно, мы также можем использовать аннотацию @PropertySources и указать массив @PropertySource . Это работает в любой поддерживаемой версии Java, а не только в Java 8 или выше:

@PropertySources({
@PropertySource("classpath:foo.properties"),
@PropertySource("classpath:bar.properties")
})
public class PropertiesWithJavaConfig {
//...
}

В любом случае стоит отметить, что в случае конфликта имени свойства последнее чтение исходного кода имеет приоритет.

3. Использование/внедрение свойств

Внедрить свойство с аннотацией @Value очень просто:

@Value( "${jdbc.url}" )
private String jdbcUrl;

Мы также можем указать значение по умолчанию для свойства:

@Value( "${jdbc.url:aDefaultUrl}" )
private String jdbcUrl;

Новый PropertySourcesPlaceholderConfigurer , добавленный в Spring 3.1 , разрешает заполнители ${…} в значениях свойств определения компонента и аннотациях @Value .

Наконец, мы можем получить значение свойства с помощью Environment API :

@Autowired
private Environment env;
...
dataSource.setUrl(env.getProperty("jdbc.url"));

4. Свойства с Spring Boot

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

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

4.1. application.properties: файл свойств по умолчанию

Boot применяет свой типичный подход к настройке файлов свойств. Это означает, что мы можем просто поместить файл application.properties в наш каталог src/main/resources , и он будет обнаружен автоматически . Затем мы можем внедрить из него любые загруженные свойства, как обычно.

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

Мы также можем настроить другой файл во время выполнения, если нам нужно, используя свойство среды:

java -jar app.jar --spring.config.location=classpath:/another-location.properties

Начиная с Spring Boot 2.3 , мы также можем указать расположение подстановочных знаков для файлов конфигурации .

Например, мы можем установить для свойства spring.config.location значение config/*/ :

java -jar app.jar --spring.config.location=config/*/

Таким образом, Spring Boot будет искать файлы конфигурации, соответствующие шаблону каталога config/*/ , за пределами нашего файла jar. Это удобно, когда у нас есть несколько источников свойств конфигурации.

Начиная с версии 2.4.0 , Spring Boot поддерживает использование файлов свойств нескольких документов , аналогично тому, как это делает YAML по дизайну:

foreach.customProperty=defaultValue
#---
foreach.customProperty=overriddenValue

Обратите внимание, что для файлов свойств трем дефисам предшествует символ комментария ( # ).

4.2. Файл свойств среды

Если нам нужно ориентироваться на разные среды, для этого есть встроенный механизм в Boot.

Мы можем просто определить файл application-environment.properties в каталоге src/main/resources , а затем установить профиль Spring с тем же именем среды.

Например, если мы определяем «промежуточную» среду, это означает, что нам нужно определить промежуточный профиль, а затем application-staging.properties .

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

4.3. Файл свойств для конкретного теста

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

Spring Boot обрабатывает это за нас, просматривая наш каталог src/test/resources во время тестового запуска . Опять же, свойства по умолчанию по-прежнему будут вводиться как обычно, но будут переопределены ими в случае коллизии.

4.4. Аннотация @TestPropertySource _

Если нам нужен более детальный контроль над тестовыми свойствами, мы можем использовать аннотацию @TestPropertySource .

Это позволяет нам устанавливать свойства теста для определенного контекста теста, имея приоритет над источниками свойств по умолчанию:

@RunWith(SpringRunner.class)
@TestPropertySource("/foo.properties")
public class FilePropertyInjectionUnitTest {

@Value("${foo}")
private String foo;

@Test
public void whenFilePropertyProvided_thenProperlyInjected() {
assertThat(foo).isEqualTo("bar");
}
}

Если мы не хотим использовать файл, мы можем указать имена и значения напрямую:

@RunWith(SpringRunner.class)
@TestPropertySource(properties = {"foo=bar"})
public class PropertyInjectionUnitTest {

@Value("${foo}")
private String foo;

@Test
public void whenPropertyProvided_thenProperlyInjected() {
assertThat(foo).isEqualTo("bar");
}
}

Мы также можем добиться аналогичного эффекта, используя аргумент свойств аннотации @SpringBootTest :

@RunWith(SpringRunner.class)
@SpringBootTest(
properties = {"foo=bar"}, classes = SpringBootPropertiesTestApplication.class)
public class SpringBootPropertyInjectionIntegrationTest {

@Value("${foo}")
private String foo;

@Test
public void whenSpringBootPropertyProvided_thenProperlyInjected() {
assertThat(foo).isEqualTo("bar");
}
}

4.5. Иерархические свойства

Если у нас есть свойства, сгруппированные вместе, мы можем использовать аннотацию @ConfigurationProperties , которая будет отображать эти иерархии свойств в графы объектов Java.

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

database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar

А затем воспользуемся аннотацией, чтобы сопоставить их с объектом базы данных:

@ConfigurationProperties(prefix = "database")
public class Database {
String url;
String username;
String password;

// standard getters and setters
}

Spring Boot снова применяет свое соглашение к подходу к настройке, автоматически сопоставляя имена свойств и их соответствующие поля. Все, что нам нужно предоставить, это префикс свойства.

Если вы хотите углубиться в свойства конфигурации, ознакомьтесь с нашей подробной статьей .

4.6. Альтернатива: файлы YAML

Spring также поддерживает файлы YAML.

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

YAML особенно хорош для иерархического хранения свойств ; следующий файл свойств:

database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar
secret: foo

является синонимом следующего файла YAML:

database:
url: jdbc:postgresql:/localhost:5432/instance
username: foo
password: bar
secret: foo

Также стоит упомянуть, что файлы YAML не поддерживают аннотацию @PropertySource , поэтому, если нам нужно использовать эту аннотацию, это ограничит нас использованием файла свойств.

Еще одним примечательным моментом является то, что в версии 2.4.0 Spring Boot изменил способ загрузки свойств из многодокументных файлов YAML. Ранее порядок их добавления основывался на порядке активации профиля. Однако в новой версии фреймворк следует тем же правилам упорядочения, которые мы указывали ранее для файлов .properties ; свойства, объявленные ниже в файле, просто переопределяют те, что выше.

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

4.7. Импорт дополнительных файлов конфигурации

До версии 2.4.0 Spring Boot позволял включать дополнительные файлы конфигурации с помощью свойств spring.config.location и spring.config.additional-location , но они имели определенные ограничения. Например, их нужно было определить перед запуском приложения (в качестве свойств среды или системы или с помощью аргументов командной строки), поскольку они использовались в начале процесса.

В упомянутой версии мы можем использовать свойство spring.config.import в файле application.properties или application.yml , чтобы легко включать дополнительные файлы. Это свойство поддерживает некоторые интересные функции:

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

Давайте посмотрим на действительный пример:

spring.config.import=classpath:additional-application.properties,
classpath:additional-application[.yml],
optional:file:./external.properties,
classpath:additional-application-properties/

Примечание: здесь мы отформатировали это свойство, используя разрывы строк просто для ясности.

Spring будет рассматривать импорт как новый документ, вставленный сразу после декларации об импорте.

4.8. Свойства из аргументов командной строки

Помимо использования файлов, мы можем передавать свойства прямо в командной строке:

java -jar app.jar --property="value"

Мы также можем сделать это через системные свойства, которые указываются перед командой -jar , а не после нее:

java -Dproperty.name="value" -jar app.jar

4.9. Свойства из переменных среды

Spring Boot также обнаружит переменные среды, рассматривая их как свойства:

export name=value
java -jar app.jar

4.10. Рандомизация значений свойств

Если нам не нужны детерминированные значения свойств, мы можем использовать RandomValuePropertySource для рандомизации значений свойств:

random.number=${random.int}
random.long=${random.long}
random.uuid=${random.uuid}

4.11. Дополнительные типы источников собственности

Spring Boot поддерживает множество источников свойств, реализуя хорошо продуманный порядок, позволяющий разумное переопределение. Стоит ознакомиться с официальной документацией , которая выходит за рамки этой статьи.

5. Конфигурация с использованием Raw Bean — PropertySourcesPlaceholderConfigurer

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

Работа с PropertySourcesPlaceholderConfigurer дает нам полный контроль над конфигурацией, но недостатком является более подробный и в большинстве случаев ненужный.

Давайте посмотрим, как мы можем определить этот компонент, используя конфигурацию Java:

@Bean
public static PropertySourcesPlaceholderConfigurer properties(){
PropertySourcesPlaceholderConfigurer pspc
= new PropertySourcesPlaceholderConfigurer();
Resource[] resources = new ClassPathResource[ ]
{ new ClassPathResource( "foo.properties" ) };
pspc.setLocations( resources );
pspc.setIgnoreUnresolvablePlaceholders( true );
return pspc;
}

6. Свойства в родительско-дочерних контекстах

Этот вопрос возникает снова и снова: что происходит, когда наше веб-приложение имеет родительский и дочерний контексты ? Родительский контекст может иметь некоторую общую базовую функциональность и bean-компоненты, а затем один (или несколько) дочерних контекстов, возможно, содержащих специфичные для сервлета bean-компоненты.

В таком случае, как лучше всего определить файлы свойств и включить их в эти контексты? И как лучше всего получить эти свойства из Spring?

Мы дадим простую разбивку.

Если файл определен в родительском контексте :

  • @Value работает в дочернем контексте : ДА
  • @Value работает в родительском контексте : ДА
  • environment.getProperty в дочернем контексте : ДА
  • environment.getProperty в родительском контексте : ДА

Если файл определен в дочернем контексте :

  • @Value работает в дочернем контексте : ДА
  • @Value работает в родительском контексте : НЕТ
  • environment.getProperty в дочернем контексте : ДА
  • environment.getProperty в родительском контексте : НЕТ

7. Заключение

В этой статье было показано несколько примеров работы со свойствами и файлами свойств в Spring.

Как всегда, весь код, поддерживающий статью, доступен на GitHub .