Spring 3 表达式语言如何与属性占位符交互?
Spring 3 引入了一种新的表达式语言< /a> (SpEL) 可以在 bean 定义中使用。语法本身是相当明确的。
目前尚不清楚的是,SpEL 如何与先前版本中已存在的属性占位符语法进行交互(如果有的话)。 SpEL 是否支持属性占位符,或者我是否必须结合两种机制的语法并希望它们结合?
让我举一个具体的例子。我想使用属性语法 ${xyz}
,但添加了 elvis 运算符 处理 ${xyz}
未定义的情况。
我尝试了以下语法但没有成功:
#{xyz?:'defaultValue'}
#{${xyz}?:'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 类:
package test;
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 表达式,它允许我在无法解析 ${xyz}
的情况下指定默认值,并且此默认值必须指定为表达式的一部分,而不是在另一个属性集中具体化。
Spring 3 has introduced a new expression language (SpEL) which can be used in bean definitions. The syntax itself is fairly well specified.
What isn't clear is how, if at all, SpEL interacts with the property placeholder syntax that was already present in prior versions. Does SpEL have support for property placeholders, or do I have to combine the syntax of both mechanisms and hope they combine?
Let me give a concrete example. I want to use the property syntax ${x.y.z}
, but with the addition of "default value" syntax as provided by the elvis operator to handle cases where ${x.y.z}
is undefined.
I've tried the following syntaxes without success:
#{x.y.z?:'defaultValue'}
#{${x.y.z}?:'defaultValue'}
The first one gives me
Field or property 'x' cannot be found
on object of type
'org.springframework.beans.factory.config.BeanExpressionContext'
which suggests that SpEL doesn't recognise this as a property placeholder.
The second syntax throws an exception saying that the placeholder is not recognised, so the placeholder resolver is being invoked, but is failing as expected, since the property is not defined.
The docs make no mention of this interaction, so either such a thing is not possible, or it's undocumented.
Anyone managed to do this?
OK, I've come up with a small, self-contained test case for this. This all works as-is:
First, the bean definitions:
<?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>
Then, the trivial bean class:
package test;
public class Bean {
String value;
public void setValue(String value) {
this.value = value;
}
}
And lastly, the test case:
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"));
}
}
The challenge - to come up with a SpEL expression in the beans file which allows me to specify a default value in cases where ${x.y.z}
cannot be resolved, and this default must be specified as part of the expression, not externalized in another property set.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
要从 SpEL 表达式访问属性占位符,可以使用以下语法:
#{'${xyz}'}
。但是,它无法解决 elvis 运算符和默认值的问题,因为当${xyz}
无法解析时它会抛出异常。但您不需要 SpEL 来声明属性的默认值:
To access property placeholder from SpEL expression, the following syntax can be used:
#{'${x.y.z}'}
. Hovewer, it can't solve your problem with elvis operator and default values, because it would throw an exception when${x.y.z}
cannot be resolved.But you don't need SpEL to declare default values for properties:
看来你错过了冒号:
It seems you missed the colon:
${myProps.item:defaultValue}
表示当myProps.item
不存在时,使用defaultValue
。这是属性占位符的默认行为。#{defaultValue}
表示字面值的 SpEL。所以,${myProps.item:#{defaultValue}}表示当
myProps.item
不存在时,计算SpEL的值,并将其分配给目标字段。示例:
${redis.auth:#{null}}
表示当redis.auth
属性不存在时,将其设置为null
。${myProps.item:defaultValue}
means that whenmyProps.item
does not exists, usedefaultValue
. That's the default behaviour of property placeholder.#{defaultValue}
means SpEL for literal value.So,
${myProps.item:#{defaultValue}}
means whenmyProps.item
does not exists, then calculate the value of SpEL, and assign it to target field.Example:
${redis.auth:#{null}}
means whenredis.auth
properties does not exists, set it tonull
.如果您只想设置占位符的默认值,请参阅此:
如果您想测试 SpEL 和占位符之间的交互,使用这个:
If you just want to set default value for placeholder, see this:
If you want to test interaction between with SpEL and placeholder, using this:
实际上 Property-Placeholder 本身就可以解决您的问题。
即,您可以使用属性
properties
在 Spring 上下文中显式指定默认设置。然后,您可以指定应使用的设置的位置,并将属性localOverride
设置为true
。在这种情况下,在外部资源中找到的所有属性(在
location
属性中指定)将覆盖默认属性(在上下文中显式定义)。希望我有所帮助。
Actually Property-Placeholder can resolve your issues itself.
I.e. you can specify default settings explicitly in Spring context, using property
properties
. Then you can specify location for settings that should be used, and set propertylocalOverride
totrue
.In such case all properties that will be found in external resources (specified in
location
property) will override default ones (explicitly defined within context).Hope I helped.
您需要添加它以使其在您的示例中运行
您的方法不起作用,因为 Spring 尝试将
${abc}
计算为具有成员的对象
与成员a
bc
会导致 NPE,因为a
不存在。You need to add this to get it running within your example
Your approach does not work, because Spring tries to evaluate
${a.b.c}
to an objecta
with memberb
with memberc
which results in a NPE becausea
does not exist.你可以:
或者
或者
...
you may:
or
or
...
我尝试了以下方法,它起作用了(虽然相当难看):
#{ myProps.getProperty('xyz')?:'Value B' }
I've tried the following and it worked (pretty ugly though):
#{ myProps.getProperty('x.y.z')?:'Value B' }
无需使用 Elvis,只需在冒号后提供默认值即可。
或者
There's no need to use Elvis, just supply the default after a colon.
or