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

Руководство по @ConfigurationProperties в Spring Boot

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

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

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

ANDROMEDA

1. Введение

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

Теперь мы собираемся более подробно изучить аннотацию @ConfigurationProperties .

2. Настройка

В этом руководстве используется довольно стандартная установка. Мы начинаем с добавления spring-boot-starter-parent в качестве родителя в наш pom.xml :

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.4</version>
<relativePath/>
</parent>

Чтобы иметь возможность проверять свойства, определенные в файле, нам также нужна реализация JSR-303, и hibernate-validator — одна из них.

Давайте также добавим его в наш pom.xml :

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.16.Final</version>
</dependency>

На странице «Начало работы с Hibernate Validator» есть более подробная информация.

3. Простые свойства

Официальная документация рекомендует изолировать свойства конфигурации в отдельные POJO .

Итак, давайте начнем с этого:

@Configuration
@ConfigurationProperties(prefix = "mail")
public class ConfigProperties {

private String hostName;
private int port;
private String from;

// standard getters and setters
}

Мы используем @Configuration , чтобы Spring создавал bean-компонент Spring в контексте приложения.

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

Платформа Spring использует стандартные установщики Java-бинов, поэтому мы должны объявить установщики для каждого из свойств.

Примечание. Если мы не используем @Configuration в POJO, нам нужно добавить @EnableConfigurationProperties(ConfigProperties.class) в основной класс приложения Spring, чтобы связать свойства с POJO:

@SpringBootApplication
@EnableConfigurationProperties(ConfigProperties.class)
public class EnableConfigurationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(EnableConfigurationDemoApplication.class, args);
}
}

Вот и все! Spring автоматически привязывает любое свойство, определенное в нашем файле свойств, которое имеет префикс mail и то же имя, что и одно из полей в классе ConfigProperties .

Spring использует некоторые смягченные правила для связывания свойств. В результате все следующие варианты привязаны к свойству hostName :

mail.hostName
mail.hostname
mail.host_name
mail.host-name
mail.HOST_NAME

Поэтому мы можем использовать следующий файл свойств для установки всех полей:

#Simple properties
mail.hostname=host@mail.com
mail.port=9000
mail.from=mailer@mail.com

3.1. Весенняя загрузка 2.2

Начиная с Spring Boot 2.2 , Spring находит и регистрирует классы @ConfigurationProperties посредством сканирования путей к классам . Сканирование @ConfigurationProperties необходимо явно включить, добавив аннотацию @ConfigurationPropertiesScan . Поэтому нам не нужно аннотировать такие классы с помощью @Component (и других мета-аннотаций, таких как @Configuration) или даже использовать @EnableConfigurationProperties:

@ConfigurationProperties(prefix = "mail") 
@ConfigurationPropertiesScan
public class ConfigProperties {

private String hostName;
private int port;
private String from;

// standard getters and setters
}

Сканер путей к классам, включенный @SpringBootApplication , находит класс ConfigProperties , хотя мы не аннотировали этот класс с помощью @Component.

Кроме того, мы можем использовать аннотацию @ConfigurationPropertiesScan для сканирования пользовательских местоположений на наличие классов свойств конфигурации: ``

@SpringBootApplication
@ConfigurationPropertiesScan("com.foreach.configurationproperties")
public class EnableConfigurationDemoApplication {

public static void main(String[] args) {
SpringApplication.run(EnableConfigurationDemoApplication.class, args);
}
}

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

4. Вложенные свойства

У нас могут быть вложенные свойства в списках, картах и классах.

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

public class Credentials {
private String authMethod;
private String username;
private String password;

// standard getters and setters
}

Нам также нужно обновить класс ConfigProperties , чтобы использовать класс List , Map и Credentials :

public class ConfigProperties {

private String host;
private int port;
private String from;
private List<String> defaultRecipients;
private Map<String, String> additionalHeaders;
private Credentials credentials;

// standard getters and setters
}

В следующем файле свойств будут установлены все поля:

#Simple properties
mail.hostname=mailer@mail.com
mail.port=9000
mail.from=mailer@mail.com

#List properties
mail.defaultRecipients[0]=admin@mail.com
mail.defaultRecipients[1]=owner@mail.com

#Map Properties
mail.additionalHeaders.redelivery=true
mail.additionalHeaders.secure=true

#Object properties
mail.credentials.username=john
mail.credentials.password=password
mail.credentials.authMethod=SHA1

5. Использование @ConfigurationProperties в методе @Bean

Мы также можем использовать аннотацию @ConfigurationProperties для методов, аннотированных @Bean .

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

Давайте создадим простой класс Item , который мы будем использовать в следующем примере:

public class Item {
private String name;
private int size;

// standard getters and setters
}

Теперь давайте посмотрим, как мы можем использовать @ConfigurationProperties в методе @Bean для привязки внешних свойств к экземпляру Item :

@Configuration
public class ConfigProperties {

@Bean
@ConfigurationProperties(prefix = "item")
public Item item() {
return new Item();
}
}

Следовательно, любое свойство с префиксом элемента будет сопоставлено с экземпляром элемента , управляемым контекстом Spring.

6. Проверка свойства

@ConfigurationProperties обеспечивает проверку свойств с использованием формата JSR-303. Это позволяет делать всевозможные опрятные вещи.

Например, сделаем свойство hostName обязательным:

@NotBlank
private String hostName;

Далее сделаем свойство authMethod длиной от 1 до 4 символов:

@Length(max = 4, min = 1)
private String authMethod;

Затем свойство порта с 1025 по 65536:

@Min(1025)
@Max(65536)
private int port;

Наконец, свойство from должно соответствовать формату адреса электронной почты:

@Pattern(regexp = "^[a-z0-9._%+-]+@[a-z0-9.-]+\\.[a-z]{2,6}$")
private String from;

Это помогает нам сократить количество условий if-else в нашем коде и делает его более чистым и лаконичным.

Если какая-либо из этих проверок не пройдена, основное приложение не запустится с IllegalStateException .

Инфраструктура Hibernate Validation использует стандартные геттеры и сеттеры Java-бинов, поэтому важно, чтобы мы объявляли геттеры и сеттеры для каждого из свойств.

7. Преобразование собственности

@ConfigurationProperties поддерживает преобразование для нескольких типов привязки свойств к соответствующим компонентам.

7.1. Продолжительность

Мы начнем с преобразования свойств в объекты Duration .

Здесь у нас есть два поля типа Duration :

@ConfigurationProperties(prefix = "conversion")
public class PropertyConversion {

private Duration timeInDefaultUnit;
private Duration timeInNano;
...
}

Это наш файл свойств:

conversion.timeInDefaultUnit=10
conversion.timeInNano=9ns

В результате поле timeInDefaultUnit будет иметь значение 10 миллисекунд, а timeInNano — значение 9 наносекунд.

Поддерживаемые единицы: ns, us, ms, s, m, h и d для наносекунд, микросекунд, миллисекунд, секунд, минут, часов и дней соответственно.

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

Мы также можем переопределить единицу измерения по умолчанию, используя @DurationUnit:

@DurationUnit(ChronoUnit.DAYS)
private Duration timeInDays;

Это соответствующее свойство:

conversion.timeInDays=2

7.2. размер данных

Точно так же Spring Boot @ConfigurationProperties поддерживает преобразование типа DataSize .

Добавим три поля типа DataSize :

private DataSize sizeInDefaultUnit;

private DataSize sizeInGB;

@DataSizeUnit(DataUnit.TERABYTES)
private DataSize sizeInTB;

Это соответствующие свойства:

conversion.sizeInDefaultUnit=300
conversion.sizeInGB=2GB
conversion.sizeInTB=4

В этом случае значение sizeInDefaultUnit будет равно 300 байтам, поскольку единицей измерения по умолчанию являются байты.

Поддерживаемые единицы: B, КБ, МБ, ГБ и ТБ. Мы также можем переопределить единицу измерения по умолчанию, используя @DataSizeUnit.

7.3. Пользовательский конвертер

Мы также можем добавить наш собственный преобразователь для поддержки преобразования свойства в определенный тип класса.

Добавим простой класс Employee :

public class Employee {
private String name;
private double salary;
}

Затем мы создадим собственный преобразователь для преобразования этого свойства:

conversion.employee=john,2000

Мы преобразуем его в файл типа Employee :

private Employee employee;

Нам нужно будет реализовать интерфейс Converter , а затем использовать аннотацию @ConfigurationPropertiesBinding для регистрации нашего пользовательского Converter :

@Component
@ConfigurationPropertiesBinding
public class EmployeeConverter implements Converter<String, Employee> {

@Override
public Employee convert(String from) {
String[] data = from.split(",");
return new Employee(data[0], Double.parseDouble(data[1]));
}
}

8. Неизменная привязка @ConfigurationProperties

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

По сути, это означает, что классы, аннотированные @ConfigurationProperties , теперь могут быть неизменяемыми .

@ConfigurationProperties(prefix = "mail.credentials")
@ConstructorBinding
public class ImmutableCredentials {

private final String authMethod;
private final String username;
private final String password;

public ImmutableCredentials(String authMethod, String username, String password) {
this.authMethod = authMethod;
this.username = username;
this.password = password;
}

public String getAuthMethod() {
return authMethod;
}

public String getUsername() {
return username;
}

public String getPassword() {
return password;
}
}

Как мы видим, при использовании @ConstructorBinding нам нужно предоставить конструктору все параметры, которые мы хотели бы связать.

Обратите внимание, что все поля ImmutableCredentials являются окончательными. Кроме того, нет методов установки.

Кроме того, важно подчеркнуть, что для использования привязки конструктора нам необходимо явно включить наш класс конфигурации либо с помощью @EnableConfigurationProperties, либо с помощью @ConfigurationPropertiesScan .

9. Запись Java 16

Java 16 представила типы записей как часть JEP 395 . Записи — это классы, выступающие в роли прозрачных носителей неизменяемых данных. Это делает их идеальными кандидатами на роль держателей конфигураций и DTO. На самом деле мы можем определить записи Java как свойства конфигурации в Spring Boot . Например, предыдущий пример можно переписать так:

@ConstructorBinding
@ConfigurationProperties(prefix = "mail.credentials")
public record ImmutableCredentials(String authMethod, String username, String password) {
}

Очевидно, что это более лаконично по сравнению со всеми этими шумными геттерами и сеттерами.

Более того, начиная с Spring Boot 2.6 , для записей с одним конструктором мы можем отказаться от аннотации @ConstructorBinding . Однако, если наша запись имеет несколько конструкторов, @ConstructorBinding все равно следует использовать для идентификации конструктора, используемого для привязки свойства.

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

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

Как обычно, код доступен на Github .