Spring的@Value注解中可以指定多个属性名吗?

Posted

技术标签:

【中文标题】Spring的@Value注解中可以指定多个属性名吗?【英文标题】:Can multiple property names be specified in Spring's @Value annotation? 【发布时间】:2018-09-14 03:32:17 【问题描述】:

我已经熟悉 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注解可以达到同样的效果吗?

编辑澄清: 我不想跟踪多个值,所以我不需要关于如何获取多个值的说明……目标是合并成一个可能有多个名称的 SINGLE 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(:)'

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

【问题讨论】:

假设可以做到这一点,如果用户同时拥有旧约定和新约定会发生什么? @pvpkiran ,在这种情况下,我会赞成新的约定而不是旧的约定。 下面的方法你可以做到。数组值:[“A”, “B”, “C”] @Value("$listOfValues") private String[] valuesArray; baeldung.com/spring-value-annotation --> 参见 spring 文档 @Value("$com.mycompany.propertygroup.propertyname:$oldconvention.property") 应该可以解决问题。 【参考方案1】:

可以使用以下@Value注解:

@Configuration
public class MyConfig 

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

如果提供了@Value 注释,则使用com.mycompany.propertygroup.propertyname,如果未提供com.mycompany.propertygroup.propertyname,则默认为oldconvention.property。如果两者均未提供,则该属性设置为 null。您可以将此默认值设置为另一个值,方法是将 null 替换为另一个所需值。

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

Spring Expression Language (SpEL) Spring Expression Language Guide

作为替代方案,您可以捕获两个值并在返回值之前进行选择:

@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;
       
    

【讨论】:

@AEvans 明白了。两者都可以(我的意见不一定适用于您的情况)。对于boolean 值,我认为您对虚假评估是正确的。为避免这种情况,您可以将值设置为Boolean,然后检查它是否为null@Value("#com.mycompany.propertygroup.propertyname != null ? com.mycompany.propertygroup.propertyname : oldconvention.property")。有关详细信息,请参阅 docs.spring.io/spring/docs/current/spring-framework-reference/… 和 baeldung.com/spring-value-defaults 的 Primitives 部分 @JustinAlbano 感谢您提供文档链接;他们帮了大忙!但是在实践中,条件 SpEL 似乎仅在定义了两个属性时才有效,而不是允许项目仅指定属性之一,上下文将无法加载,因为 SpEL 错误无法执行其中一个属性替换... @AEvans 尝试:@Value("#'$com.mycompany.propertygroup.propertyname:$oldconvention.propertyname:null'")。我已经用一个模拟应用程序对其进行了测试,如果它存在,它能够获得com.mycompany.propertygroup.propertyname 的值;如果不是,则使用oldconvention.propertyname 的值;如果两者都不存在,则返回 null。让我知道它是否适用于您的应用程序。 我的最终解决方案:/* Use the fully qualified name of the property if available, otherwise, * look for the old name convention for backwards compatibility. Defaults * to null if neither property is found. */ @Value("#'$com.mycompany.propertygroup.propertyname:$oldconvention.propertyname:'") private String myValue; @AEvans 有道理。我已经更新了使用空默认值的解决方案。【参考方案2】:

不,我相信这是不可能的,但是是的,您可以将属性定义为逗号分隔。例如 com.mycompany.propertygroup.propertyname=value1,value2,value3

您可以像这样在String[] 上注释@Value 而不是接收字符串:

@Value("#'$com.mycompany.propertygroup.propertyname'.split(',')")
private String[] propertyNames;

另一种方式,您也可以将键和值作为逗号分隔的字符串存储在属性文件中,并使用@Value 注释可以映射到Map,例如,您希望组名作为键,值作为组详细信息所以在属性文件中你可以像这样存储字符串

group.details.property= 'group1':'group1.details','group2':'group2.details'

你可以将@Value注释为

@Value("#$group.details.property")
private Map<String, String> groupMap;

【讨论】:

目的不是检索多个属性值,而是将多个属性名称解析为一个值。 或者也许真的不可能......我认为接受的答案有效,但我最初只测试了这两个属性。一旦缺少其中一个属性,SpEL 就无法评估。我不接受使用 SpEL 的答案,并用这些新发现更新了问题。【参考方案3】:

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

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

【讨论】:

所以起初我认为这是成功的,因为它似乎有效......但我只测试了旧约定和新约定属性都存在时......一旦其中一个不存在t 指定时,SPEL 无法对缺少的任何属性进行评估,并出现“无法解析属性占位符”错误。

以上是关于Spring的@Value注解中可以指定多个属性名吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring @Value 注解中正确指定默认值?

spring注解@service("service")括号中的service有什么用

Spring 注解

spring 注解是有啥作用

定义2:spring中的@resource

Spring注解之@Lazy注解