Spring 3引入了一个新的expression language(SpEL),可用于bean定义。语法本身已经明确指定。
不清楚的是,SpEL如何与先前版本中已存在的属性占位符语法进行交互。 SpEL是否支持属性占位符,或者我是否必须结合两种机制的语法并希望它们结合起来?
让我举一个具体的例子。我想使用属性语法${x.y.z}
,但添加了elvis operator提供的“默认值”语法来处理${x.y.z}
未定义的情况。
我尝试了以下语法但没有成功:
#{x.y.z?:'defaultValue'}
#{${x.y.z}?:'defaultValue'}
第一个给了我
无法找到字段或属性“x” 对象的类型 'org.springframework.beans.factory.config.BeanExpressionContext'
表明SpEL不会将此识别为属性占位符。
第二种语法抛出一个异常,说明占位符未被识别,因此占位符解析器被调用,但由于未定义属性而失败,因为该属性未定义。
文档没有提及这种交互,所以这样的事情是不可能的,或者它没有文档。
有人设法做到了吗?
好的,我已经为此提出了一个小型,独立的测试用例。这一切都按原样运作:
首先,bean定义:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<context:property-placeholder properties-ref="myProps"/>
<util:properties id="myProps">
<prop key="x.y.z">Value A</prop>
</util:properties>
<bean id="testBean" class="test.Bean">
<!-- here is where the magic is required -->
<property name="value" value="${x.y.z}"/>
<!-- I want something like this
<property name="value" value="${a.b.c}?:'Value B'"/>
-->
</bean>
</beans>
然后,琐碎的bean类:
包裹测试;
public class Bean {
String value;
public void setValue(String value) {
this.value = value;
}
}
最后,测试用例:
package test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class PlaceholderTest {
private @Resource Bean testBean;
@Test
public void valueCheck() {
assertThat(testBean.value, is("Value A"));
}
}
挑战 - 在beans文件中提出一个SpEL表达式,允许我在无法解析${x.y.z}
的情况下指定默认值,并且必须指定此默认值作为表达式的一部分,不在另一个属性集中外化。
答案 0 :(得分:27)
要从SpEL表达式访问属性占位符,可以使用以下语法:#{'${x.y.z}'}
。 Hovewer,它无法用elvis运算符和默认值解决你的问题,因为当${x.y.z}
无法解析时会抛出异常。
但是您不需要SpEL来声明属性的默认值:
<context:property-placeholder location="..." properties-ref="defaultValues"/>
<bean id = "defaultValues" class = "org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="x.y.z">ZZZ</prop>
</props>
</property>
</bean>
<bean ...>
<property name = "..." value = "${x.y.z}" />
</bean>
答案 1 :(得分:11)
好像你错过了冒号:
#{ ${x.y.z} ?: 'defaultValue' }
答案 2 :(得分:8)
如果您只想为占位符设置默认值,请参阅this:
<property name="value" value="${x.y.z:defaultValue}"/>
如果要测试SpEL和占位符之间的交互,请使用以下命令:
<!-- set value "77-AA-BB-CC-88" when property "x.y.z" not exist -->
<property name="value" value="77-#{'AA-${x.y.z:BB}-CC'}-88"/>
答案 3 :(得分:8)
${myProps.item:defaultValue}
表示如果myProps.item
不存在,请使用defaultValue
。这是属性占位符的默认行为。
#{defaultValue}
表示SpEL的字面值。
因此,${myProps.item:#{defaultValue}}
表示当myProps.item
不存在时,则计算SpEL的值,并将其分配给目标字段。
示例:
${redis.auth:#{null}}
表示当redis.auth
属性不存在时,请将其设置为null
。
答案 4 :(得分:3)
实际上,Property-Placeholder可以自行解决您的问题。
即您可以使用属性properties
在Spring上下文中显式指定默认设置。然后,您可以指定应使用的设置的位置,并将属性localOverride
设置为true
。
在这种情况下,将在外部资源中找到的所有属性(在location
属性中指定)将覆盖默认属性(在上下文中明确定义)。
希望我帮助过。
答案 5 :(得分:3)
您需要添加此内容才能让它在您的示例中运行
<bean id="testBean" class="elvis.Bean">
<!-- here is where the magic is required
<property name="value" value="${x.y.z}"/>
-->
<!-- I want something like this -->
<property name="value" value="#{myProps.get('a.b.c')?:'Value B'}"/>
</bean>
您的方法不起作用,因为Spring会尝试将${a.b.c}
评估为具有成员a
的成员b
的对象c
,这会导致NPE,因为{{1} }}不存在。
答案 6 :(得分:1)
<bean id="testBean" class="test.Bean">
<!-- if 'a.b.c' not found, then value="Value B" --->
<property name="value" value="${a.b.c:Value B}"/>
</bean>
或
...
<!-- if 'a.b.c' not found , but 'a.b' found ,then value=${a.b}
if 'a.b' also not found , then value="a"
-->
<property name="value" value="${a.b.c:${a.b:a}"/>
...
或 ...
<!-- if 'a.b.c' not found , but 'a.b' found ,then value=${a.b}
if 'a.b' also not found , then value="a"
-->
<property name="value" value="#{ '${a.b.c:}' ?: '${a.b:a}' }"/>
...
答案 7 :(得分:0)
我尝试了以下内容并且有效(虽然非常难看):
#{ myProps.getProperty('x.y.z')?:'Value B' }
答案 8 :(得分:0)
无需使用Elvis,只需在冒号后提供默认值即可。
@Value("${my.connection.timeout:5000}")
private int myTimeoutMillis;
或
@Retryable(maxAttemptsExpression = "#{${my.max.attempts:10}}")
public void myRetryableMethod() {
// ...
}