Обратная совместимость при изменении внешней конфигурации Spring Boot

Существует ли рекомендуемый способ внесения реструктуризации/переименования во внешнюю конфигурацию с сохранением обратной совместимости для потребителей, которые все еще полагаются на старую структуру конфигурации?

Например, если библиотека использовала следующую структуру конфигурации, определенную через @ConfigurationProperties в прошлом:

old-properties:
  an:
    old-property: true
  another:
    custom-property: 1234

Новая версия этой библиотеки переопределяет конфигурацию примерно так:

my-library:
  a-property: true
  another-property: 1234

Есть ли хороший способ отказаться от старой структуры, сохраняя при этом совместимость с существующими потребителями в течение некоторого времени? Потребители, использующие новую версию библиотеки, должны по-прежнему иметь возможность использовать old-properties.an.old-property и автоматически сопоставлять ее с my-library.a-property.

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

Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
Что такое управление транзакциями JDBC и как оно используется для поддержания согласованности данных?
Что такое управление транзакциями JDBC и как оно используется для поддержания согласованности данных?
Управление транзакциями JDBC - это мощная функция, которая позволяет рассматривать группу операций с базой данных как единую единицу работы. Оно...
Выполнение HTTP-запроса с помощью Spring WebClient: GET
Выполнение HTTP-запроса с помощью Spring WebClient: GET
WebClient - это реактивный веб-клиент, представленный в Spring 5. Это реактивное, неблокирующее решение, работающее по протоколу HTTP/1.1.
Gradle за прокси-сервером
Gradle за прокси-сервером
Создайте проект Gradle под сетевым прокси.
5
0
1 245
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Смотрите здесь для основного примера. Следующий фрагмент из CassandraProperties.java показывает реальный вариант использования, в котором spring.data.cassandra.cluster-name устарела в пользу spring.data.cassandra.session-name. Обратная совместимость обеспечивается простым вызовом геттера/сеттера для свойства замены в геттере/сеттере для устаревшего свойства:

public String getSessionName() {
    return this.sessionName;
}

public void setSessionName(String sessionName) {
    this.sessionName = sessionName;
}

@Deprecated
@DeprecatedConfigurationProperty(replacement = "spring.data.cassandra.session-name")
public String getClusterName() {
    return getSessionName();
}

@Deprecated
public void setClusterName(String clusterName) {
    setSessionName(clusterName);
}

Чтобы добиться того же поведения для свойств, которые не сопоставлены с bean-компонентом @ConfigurationProperties, вы можете вручную указать их в META-INF/additional-spring-configuration-metadata.json и добавить зависимость времени выполнения от org.springframework.boot:spring-boot-properties-migrator. См. Spring Boot docs для справки.

Следующий фрагмент из spring-boot-autoconfigure показывает реальный вариант использования, в котором server.servlet.path устарела в пользу spring.mvc.servlet.path. Обратная совместимость обеспечивается PropertiesMigrationListener, который «автоматически переименовывает ключи, имеющие совпадающую замену, и регистрирует отчет о том, что было обнаружено»:

    {
      "name": "server.servlet.path",
      "type": "java.lang.String",
      "description": "Path of the main dispatcher servlet.",
      "defaultValue": "/",
      "deprecation": {
        "replacement": "spring.mvc.servlet.path",
        "level": "error"
      }
    },

Если вы установите устаревшее свойство server.servlet.path=/foo, замещающее свойство @Value("${spring.mvc.servlet.path}") будет оценено как /foo, а уведомление об устаревании будет зарегистрировано при запуске.

Я должен был упомянуть, что я знаю (и уже использую) дополнительные метаданные, чтобы пометить эти свойства как устаревшие, и что я беспокоюсь только о поддержке обеих версий в течение некоторого времени, чтобы упростить миграцию. Спасибо за подсказку с @DeprecatedConfigurationProperty, я знал только про additional-spring-configuration-metadata.json способ.

maff 22.12.2020 12:18
Ответ принят как подходящий

Я изучил, как Spring Boot обрабатывал фазу устаревания для файла logging.file (который был заменен на logging.file.name), и поскольку они реализовали откат непосредственно в коде, я решил попробовать что-то подобное, создав новый @ConfigurationProperties в методе @Bean, который обрабатывает установка значений из старых имен свойств, если они доступны.

Учитывая, что новая структура конфигурации выглядит следующим образом (для краткости используется Lombok):

import lombok.Data;

@Data
public class MyLibraryConfigurationProperties {

    private String aProperty;
    private String anotherProperty;

}

Метод @Bean теперь считывает старое значение и применяет его к свойствам:

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

@Configuration
public class MyLibraryConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "my-library")
    public MyLibraryConfigurationProperties myLibraryConfigurationProperties(Environment environment) {
        MyLibraryConfigurationProperties config = new MyLibraryConfigurationProperties();

        // fallback to old property if available
        if (environment.containsProperty("old-properties.an.old-property")) {
            // here we could also log warnings regarding the deprecation
            config.setAProperty(environment.getProperty("old-properties.an.old-property"));
        }

        return config;
    }
}

Если новое значение также установлено через конфигурацию, оно переопределит значение, установленное из старого свойства.

Другие вопросы по теме