更改 Spring Boot 外部化配置时的向后兼容性

Posted

技术标签:

【中文标题】更改 Spring Boot 外部化配置时的向后兼容性【英文标题】:Backwards compatibility when changing Spring Boot externalized configuration 【发布时间】:2021-03-29 11:16:30 【问题描述】:

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

例如,给定一个库使用了过去通过@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

我知道使用 additional configuration metadata 将属性标记为已弃用的功能,但我明确地在寻找一种方法来支持这两个版本以简化迁移。

【问题讨论】:

【参考方案1】:

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

有关基本示例,请参阅here。以下来自CassandraProperties.java 的 sn-p 显示了一个实际用例,其中 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);

要为未映射到@ConfigurationProperties bean 的属性实现相同的行为,您可以在META-INF/additional-spring-configuration-metadata.json 中手动指定它们,并在org.springframework.boot:spring-boot-properties-migrator 上添加运行时依赖项。参考Spring Boot docs。

spring-boot-autoconfigure 中的以下 sn-p 显示了一个实际用例,其中 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 的方式。【参考方案2】:

我研究了 Spring Boot 如何处理 deprecation phase for logging.file(已被 logging.file.name 取代),当他们直接在代码中实现回退时,我决定通过在 @Bean 中创建新的 @ConfigurationProperties 来尝试类似的事情处理从旧属性名称设置值(如果可用)的方法。

鉴于新的配置结构如下所示(为简洁起见,使用 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 设置的,它将覆盖从旧属性设置的值。

【讨论】:

以上是关于更改 Spring Boot 外部化配置时的向后兼容性的主要内容,如果未能解决你的问题,请参考以下文章

使用 Core Data 时的向后兼容性

使用 iOS 4.0 库时的向后兼容性

玩转Spring Boot 自定义配置导入XML配置与外部化配置

Spring Boot 2.0 Prometheus 向后兼容

spring boot使用nacos作为配置中心实践

Spring Boot 简介