是否可以在Spring的@Value注释中指定多个属性名称?

2022-09-04 02:46:45

我已经熟悉了Spring注释的基本行为,将字段设置为项目属性的值,如下所示:@Value

项目的属性文件

foo.bar=value

项目的配置类

@Configuration
public class MyConfig {
    @Value("${foo.bar}")
    private String myValue;
}

但是,我正在尝试使用条件配置制作SpringBoot初学者项目,并希望将属性名称标准化为有用的内容,例如“com.mycompany.propertygroup.propertyname”,但是为了简化过渡并鼓励采用,我想在一段时间内支持旧的属性名称,因此想知道是否有某种方法可以允许多个属性名称设置相同的字段?例如:

我的理论启动器配置

@Configuration
public class MyConfig {
    @Value("${com.mycompany.propertygroup.propertyname}" || "${oldconvention.property}")
    private String myValue;
}

项目A的财产

oldconvention.property=value

项目 B 的财产

com.mycompany.propertygroup.propertyname=value

我似乎找不到任何文档或SO答案,关于这是否可能以及如何实现它......所以我想知道这是否可能,或者如果不可能,是否有注释的替代方案可用于实现相同的效果?@Value

编辑以澄清:我不想跟踪多个值,所以我不需要有关如何获取多个值的说明...目标是合并为可能具有多个名称的单个值。在实践中,每个使用起始程序的项目只有一个名称值...只有在极少数情况下,当有人可能忘记删除旧属性时,才会使用每个属性名称(无论如何,它可能具有相同的值)。在这种情况下,新约定名称-值将是唯一使用的约定值。

更新

虽然提供的 SpEL 表达式答案在两个属性都存在时工作,但当只存在一个属性名称时,应用程序上下文无法加载。例:

更新的配置类

@Value("#{'${com.mycompany.propertygroup.propertyname}' != null ? '${com.mycompany.propertygroup.propertyname}' : '${oldconvention.propertyname}'}"
private String myProperty;

更新的属性文件

com.mycompany.propertygroup.propertyname=somevalue

错误

Caused by: java.lang.IllegalArgumentException: 
Could not resolve placeholder 'oldconvention.propertyname' in value
"#{'${com.mycompany.propertygroup.propertyname}' != null ? '${com.mycompany.propertygroup.propertyname}' : '${oldconvention.propertyname}'}"

要求两个属性名称都存在会破坏目的,即允许实现项目使用旧约定或新约定配置此启动器...

另一个更新...

我一直在玩SpEL表达式,并且我已经让条件检查在属性存在时和不存在时工作,但是我在事后在属性解析方面遇到了麻烦。我认为问题在于,属性默认值和复杂的 SpEL 表达式不能很好地协同工作。

@Value("#{${com.mycompany.propertygroup.propertyname:null} != null ? '${com.mycompany.propertygroup.propertyname}' : '${oldconvention.propertyname}'}")
private String myProperty;

当我的 SpEL 像上面这样编写时,我得到一个无法解析属性占位符异常,这意味着两个属性都必须存在才能计算 SpEL 表达式。所以我开始思考,我可以使用我见过的默认属性语法来解决可选属性:@Value("${myoptionalproperty:defaultValue}")

因此,以下是我将默认属性分辨率与SpEL表达式相结合的尝试:

@Value("#{${com.mycompany.propertygroup.propertyname:null} != null ? '${com.mycompany.propertygroup.propertyname:}' : '${oldconvention.propertyname:}'}")
private String myProperty;

使用默认属性表示法时,我不断收到此错误:

org.springframework.expression.spel.SpelParseException: 
EL1041E: After parsing a valid expression, there is still more data in the expression: 'colon(:)'

当我在谷歌上搜索这个错误时,流行的答案是属性必须用单引号括起来,以便它们计算为字符串......但它们已经包装好了(除了第一个。我必须解开那个,因为我希望它计算为空检查的文字null)。因此,我认为当默认值包装在拼写表达式中时,它们不能与属性一起使用。实际上,我只见过只使用纯属性持有者设置注释时的默认属性集,而我在SpEL表达式中使用的所有属性都没有默认集。@Value


答案 1

您可以使用以下批注:@Value

@Configuration
public class MyConfig {

    @Value("#{'${com.mycompany.propertygroup.propertyname:${oldconvention.propertyname:}}'}")
    private String myValue;
}

此批注使用 if 提供,默认为 if 未提供。如果两者都未提供,则该属性设置为 。您可以通过替换为另一个所需值来将此默认值设置为另一个值。@Valuecom.mycompany.propertygroup.propertynameoldconvention.propertycom.mycompany.propertygroup.propertynamenullnull

有关详细信息,请参阅以下内容:


或者,您可以捕获这两个值,并在返回值之前进行选择:

@Configuration
public class MyConfig {

    @Value("${com.mycompany.propertygroup.propertyname:}")
    private String newValue;

    @Value("${oldconvention.propertyname:}")
    private String oldValue;

    public String getValue() {

        if (newValue != null && !newValue.isEmpty()) {
            // New value is provided
            System.out.println("New Value: " + newValue);
            return newValue;
        }
        else {
            // Default to the old value
            return oldValue;
       }
    }
}

答案 2

使用 SPEL 是解决此问题的最佳方法。这应该有效

@Value("#{'${com.mycompany.propertygroup.propertyname}' != null ? '${com.mycompany.propertygroup.propertyname}' : '${oldconvention.property}'}")
private String myValue;