«1. Обзор

При написании приложения Spring Boot полезно сопоставлять свойства конфигурации с Java-бинами. Но как лучше всего задокументировать эти свойства?

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

2. Метаданные конфигурации

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

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

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

3. Зависимости

Чтобы сгенерировать эти метаданные конфигурации, мы будем использовать процессор конфигурации из зависимости spring-boot-configuration-processor.

Итак, давайте добавим зависимость как необязательную:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <version>2.1.6.RELEASE</version>
    <optional>true</optional>
</dependency>

Эта зависимость предоставит нам обработчик аннотаций Java, вызываемый при сборке нашего проекта. Мы подробно поговорим об этом позже.

Рекомендуется добавить зависимость как необязательную в Maven, чтобы предотвратить применение @ConfigurationProperties к другим модулям, которые использует наш проект.

4. Пример свойств конфигурации

Чтобы увидеть процессор в действии, давайте представим, что у нас есть несколько свойств, которые нам нужно включить в наше приложение Spring Boot через Java-бин:

@Configuration
@ConfigurationProperties(prefix = "database")
public class DatabaseProperties {
	
    public static class Server {

        private String ip;
        private int port;

        // standard getters and setters
    }
	
    private String username;
    private String password;
    private Server server;
	
    // standard getters and setters
}

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

Давайте добавим пару этих свойств в файл свойств. В этом случае мы назовем его databaseproperties-test.properties:

#Simple Properties
database.username=baeldung
database.password=password

И, чтобы быть уверенным, мы также добавим тест, чтобы убедиться, что мы все выстроились в очередь:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AnnotationProcessorApplication.class)
@TestPropertySource("classpath:databaseproperties-test.properties")
public class DatabasePropertiesIntegrationTest {

    @Autowired
    private DatabaseProperties databaseProperties;

    @Test
    public void whenSimplePropertyQueriedThenReturnsPropertyValue() 
      throws Exception {
        Assert.assertEquals("Incorrectly bound Username property", 
          "baeldung", databaseProperties.getUsername());
        Assert.assertEquals("Incorrectly bound Password property", 
          "password", databaseProperties.getPassword());
    }
    
}

~~ ~ Мы также добавили вложенные свойства database.server.id и database.server.port через внутренний класс Server. Мы должны добавить внутренний класс Server, а также полевой сервер с собственным геттером и сеттером.

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

@Test
public void whenNestedPropertyQueriedThenReturnsPropertyValue() 
  throws Exception {
    Assert.assertEquals("Incorrectly bound Server IP nested property",
      "127.0.0.1", databaseProperties.getServer().getIp());
    Assert.assertEquals("Incorrectly bound Server Port nested property", 
      3306, databaseProperties.getServer().getPort());
}

Хорошо, теперь мы готовы использовать процессор.

5. Генерация метаданных конфигурации

Ранее мы упоминали, что процессор конфигурации создает файл – он использует обработку аннотаций.

Итак, после компиляции нашего проекта мы увидим файл с именем spring-configuration-metadata.json внутри target/classes/META-INF:

{
  "groups": [
    {
      "name": "database",
      "type": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server",
      "type": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceMethod": "getServer()"
    }
  ],
  "properties": [
    {
      "name": "database.password",
      "type": "java.lang.String",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server.ip",
      "type": "java.lang.String",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server"
    },
    {
      "name": "database.server.port",
      "type": "java.lang.Integer",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "defaultValue": 0
    },
    {
      "name": "database.username",
      "type": "java.lang.String",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties"
    }
  ],
  "hints": []
}

Далее давайте посмотрим, как меняются аннотации в наших Java-бинах влияют на метаданные.

5.1. Дополнительная информация о метаданных конфигурации

Во-первых, давайте добавим комментарии JavaDoc на сервер.

Во-вторых, давайте зададим значение по умолчанию для поля database.server.port и, наконец, добавим аннотации @Min и @Max:

public static class Server {

    /**
     * The IP of the database server
     */
    private String ip;

    /**
     * The Port of the database server.
     * The Default value is 443.
     * The allowed values are in the range 400-4000.
     */
    @Min(400)
    @Max(800)
    private int port = 443;

    // standard getters and setters
}

Если мы проверим файл spring-configuration-metadata.json Теперь мы увидим эту дополнительную информацию:

{
  "groups": [
    {
      "name": "database",
      "type": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server",
      "type": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties",
      "sourceMethod": "getServer()"
    }
  ],
  "properties": [
    {
      "name": "database.password",
      "type": "java.lang.String",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties"
    },
    {
      "name": "database.server.ip",
      "type": "java.lang.String",
      "description": "The IP of the database server",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server"
    },
    {
      "name": "database.server.port",
      "type": "java.lang.Integer",
      "description": "The Port of the database server. The Default value is 443.
        The allowed values are in the range 400-4000",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties$Server",
      "defaultValue": 443
    },
    {
      "name": "database.username",
      "type": "java.lang.String",
      "sourceType": "com.baeldung.autoconfiguration.annotationprocessor.DatabaseProperties"
    }
  ],
  "hints": []
}

Мы можем проверить различия с помощью полей database.server.ip и database.server.port. Действительно, дополнительная информация весьма полезна. В результате разработчикам и IDE намного проще понять, что делает каждое свойство.

Мы также должны убедиться, что запускаем сборку для получения обновленного файла. В Eclipse, если мы отметим параметр Build Automatically, каждое действие сохранения будет запускать сборку. В IntelliJ мы должны запускать сборку вручную.

5.2. Понимание формата метаданных

Давайте подробнее рассмотрим файл метаданных JSON и обсудим его компоненты.

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

Свойства — это элементы конфигурации, для которых мы можем указать значение. Эти свойства задаются в файлах .properties или .yml и могут содержать дополнительную информацию, например значения по умолчанию и проверки, как мы видели в примере выше.

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

Каждый компонент в метаданных конфигурации имеет свои собственные атрибуты для более подробного объяснения свойств конфигурации.

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

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

Мы привели пример сгенерированных метаданных конфигурации и подробно объяснили их формат и компоненты.

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

Как всегда, все фрагменты кода, упомянутые в этой статье, можно найти в нашем репозитории GitHub.