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注解中可以指定多个属性名吗?的主要内容,如果未能解决你的问题,请参考以下文章