3

是否有推荐的方法将重组/重命名引入外部配置,同时为仍然依赖旧配置结构的消费者保持向后兼容性?

例如,给定一个库使用@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.

我知道使用其他配置元数据将属性标记为已弃用的功能,但我明确地寻找一种方法来支持这两个版本以简化迁移。

4

2 回答 2

4

Spring Boot Configuration Processor@DeprecatedConfigurationProperty为此目的提供了注解。生成的元数据文件将包含任何原因/替换说明,如果使用带注释的属性,则会记录适当的弃用警告。

有关基本示例,请参见此处CassandraProperties.java中的以下代码段显示了一个实际用例,在该用例中spring.data.cassandra.cluster-name已弃用spring.data.cassandra.session-name. 通过简单地调用 getter/setter 来处理不推荐使用的属性的 getter/setter 中的替换属性来处理向后兼容性:

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 文档以供参考。

以下来自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"
      }
    },

如果您设置 deprecated 属性server.servlet.path=/foo,则替换属性@Value("${spring.mvc.servlet.path}")将评估为/foo,并且将在启动时记录弃用通知。

于 2020-12-19T00:16:18.980 回答
1

我研究了 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;
    }
}

如果新值也是通过 config 设置的,它将覆盖从旧属性设置的值。

于 2020-12-22T11:42:27.587 回答