[spring-projects/spring-boot]声明的默认值不会被属性引用替换

2024-07-08 465 views
9

假设您声明一个如下配置属性类:

@ConstructorBinding
@ConfigurationProperties("sample")
class SampleProperties {

  SampleProperties(@DefaultValue("${user.home}/foo") String location) { … }
}

如果应用程序现在将属性配置sample.location${user.home}/bar,则构造函数将使用 进行调用/Users/…/bar,即对用户主文件夹的动态引用已被实际值替换。如果应用程序未配置显式值,则传递给构造函数的值实际上是${user.home}/foo,即对用户主目录的引用未解析。

回答

9

这是https://github.com/spring-projects/spring-boot/issues/23164的重复。事后看来,我不应该关闭那个,而应该用它来更新文档。我们可以利用这个问题至少做到这一点。我们还可以利用这个机会来确认我们想要保留当前的行为。就目前情况而言,@DefaultValue当字段声明为默认值时,行为与可变属性的工作方式一致。

6

那么那可能也是个错误?我的意思是,对我来说这似乎是一个合理的用例。另一方面,我想你总是可以添加sample.location=${user.home}/fooapplication.properties

2

如果该属性是库的一部分,那就有点不方便了。默认值的意义不就是用户不必首先提供它吗?

1

那么那可能也是一个错误?

我不这么认为。除了建议默认值为常量之外,如果我们尊重字段默认值中的占位符,那么绑定性能将受到巨大影响。对于类上的每个属性@ConfigurationProperties,无论是否有属性绑定到它,我们都必须找到其支持字段,检索其值,执行占位符解析,然后再次设置该值。

1

我不确定我是否理解。@DefaultValue只能使用构造函数绑定或设置器对配置属性类的构造函数参数使用,对吗?只有当注释存在且未明确配置值时,才会进行该处理。如果用户通过提供值,也会发生这种情况application.properties。我遗漏了什么吗?

2

@DefaultValue同意 - 对我来说,和具有初始化程序的字段之间的等价性似乎是错误的。

7

我认为 Dave 的建议“那可能也是一个错误”是对“的行为@DefaultValue与当字段使用默认值声明时可变属性的工作方式一致”的回应。我想说的是,如果字段的初始化程序使用占位符(例如private String credentials = "${username}:${password}"),除非该值被覆盖,否则它将不会因属性绑定而发生变化。

2

我明白了。我甚至没有想那么远。我只是指处理注释中声明的字符串值。

9

从目前情况来看,所需的行为可以通过基于 setter 的绑定来实现:

@ConfigurationProperties("sample")
public class SampleMutableProperties {

    /**
     * Location to use. Defaults to a directory named example in the user's home directory.
     */
    private String location = System.getProperty("user.home") + "/example";

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }

}

使用如下不可变配置属性也可以实现同样的效果:

@ConstructorBinding
@ConfigurationProperties("sample")
public class SampleImmutableProperties {

    /**
     * Location to use. Defaults to a directory named example in the user's home directory.
     */
    private final String location;

    public SampleImmutableProperties(String location) {
        this.location = (location != null) ? location : System.getProperty("user.home") + "/example";
    }

    public String getLocation() {
        return location;
    }

}

的 javadoc 声明@DefaultValue它“可用于在绑定到不可变属性时指定默认值”。当您尝试将其转换SampleMutableProperties为不可变时,这并不准确。您必须在构造函数中手动执行默认值,如上所示。

知道您只有一个字符串可以使用,尝试使用@DefaultValue("${user.home}/example")(特别是如果您已经熟悉 Spring)对我来说似乎很合乎逻辑,但它不会像希望的那样工作,因为${user.home}会保持原样。如果它像 @odrotbohm 预期的那样工作并执行属性占位符解析,它会更简洁,并且比字段初始化器更有优势,能够自动为属性记录一个合理的默认值。

6

如果我们改变这一点,我们面临的一个问题是,如果用户想要使用"${...} 而不进行占位符解析,该怎么办。我认为我们有办法做到这一点,我们还需要考虑向后兼容性。

我个人倾向于保持现状并改进 javadoc。我非常喜欢与字段当前工作方式的一致性。@Value如果我们解析占位符,我也有点担心重叠。

考虑到在这个例子中,我认为我们真正想要的是用户主文件夹,根据属性源的配置方式,属性解析可能会或可能不会提供该文件夹。用户可能(但不太可能)删除了源systemProperties。他们也可能USER_HOME设置了环境变量或user.homeapplication.properties。这可能没问题,但这是需要考虑的事情。

5

话虽如此,这个名字@DefaultValue确实让人认为其行为类似于@Value。?

5

如果我们改变这一点,我们面临的一个问题是,如果用户想要使用"${...} 而不进行占位符解析,该怎么办。我认为我们有办法做到这一点,我们还需要考虑向后兼容性。

有人可以在属性文件中声明属性时实现这一点吗(也许通过转义$)?那么,它在注释中可以/应该以相同的方式工作吗?最终,我假设属性文件中值的显式配置与注释属性定义之间存在对称性。您概述的有关环境的挑战是否也适用于这两种情况?

1

我很确定我们没有$在属性文件中提供转义。我以为我们有一个关于此问题的未决问题,但我找不到它。

是的,环境的挑战也适用于属性文件。

3

转义相当重要。我很惊讶我们竟然没有明确支持它,就走到了这一步。但@odrotbohm 说得对:这并不是 的一个问题@DefaultValue

1

我们讨论了这个问题,并得出结论,我们希望将 的行为与在或@DefaultValue中写入相同值时发生的行为保持一致。目前,您可以进行转换(例如,对于 a ,将变为3 周),但不会进行占位符替换。为了保持一致性,我们希望两者兼而有之。application .propertiesapplication.YAML"3w"DurationDuration

我们对向后兼容性有些担忧,因此必须等到 3.x。我们已重新开启 #23164 以在此期间改进文档。