更改 Spring 引导外部化配置时的向后兼容性
Backwards compatibility when changing Spring Boot externalized configuration
是否有推荐的方法将 restructurings/renamings 引入外部化配置,同时为仍然依赖旧配置结构的消费者保持向后兼容性?
例如,给定一个库过去使用了通过 @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 将 属性 标记为已弃用的功能,但我正在明确寻找一种方法来支持这两个版本以简化迁移。
Spring 引导配置处理器为此提供了一个 @DeprecatedConfigurationProperty
注释。生成的元数据文件将包含任何 reason/replacement 注释,如果使用带注释的 属性,则会导致记录适当的弃用警告。
参见 here for a basic example. The following snippet from 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);
}
要为未映射到 @ConfigurationProperties
bean 的属性实现相同的行为,您可以在 META-INF/additional-spring-configuration-metadata.json
中手动指定它们并添加对 org.springframework.boot:spring-boot-properties-migrator
的运行时依赖性。请参阅 Spring Boot docs 以供参考。
以下摘自 spring-boot-autoconfigure shows a real-world use case in which server.servlet.path
was deprecated in favor of spring.mvc.servlet.path
. Backwards compatibility is handled by the PropertiesMigrationListener 的代码片段,其中 “自动重命名具有匹配替换项的键并记录[s] 所发现内容的报告。”:
{
"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
,并且将记录弃用通知启动。
我研究了 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;
}
}
如果新值也通过配置设置,它将覆盖旧 属性 设置的值。
是否有推荐的方法将 restructurings/renamings 引入外部化配置,同时为仍然依赖旧配置结构的消费者保持向后兼容性?
例如,给定一个库过去使用了通过 @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 将 属性 标记为已弃用的功能,但我正在明确寻找一种方法来支持这两个版本以简化迁移。
Spring 引导配置处理器为此提供了一个 @DeprecatedConfigurationProperty
注释。生成的元数据文件将包含任何 reason/replacement 注释,如果使用带注释的 属性,则会导致记录适当的弃用警告。
参见 here for a basic example. The following snippet from 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);
}
要为未映射到 @ConfigurationProperties
bean 的属性实现相同的行为,您可以在 META-INF/additional-spring-configuration-metadata.json
中手动指定它们并添加对 org.springframework.boot:spring-boot-properties-migrator
的运行时依赖性。请参阅 Spring Boot docs 以供参考。
以下摘自 spring-boot-autoconfigure shows a real-world use case in which server.servlet.path
was deprecated in favor of spring.mvc.servlet.path
. Backwards compatibility is handled by the PropertiesMigrationListener 的代码片段,其中 “自动重命名具有匹配替换项的键并记录[s] 所发现内容的报告。”:
{
"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
,并且将记录弃用通知启动。
我研究了 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;
}
}
如果新值也通过配置设置,它将覆盖旧 属性 设置的值。