53

Spring 3 引入了一种新的表达式语言(SpEL),可以在 bean 定义中使用。语法本身是相当明确的。

尚不清楚的是,如果有的话,SpEL 如何与先前版本中已经存在的属性占位符语法进行交互。SpEL 是否支持属性占位符,或者我是否必须结合两种机制的语法并希望它们结合?

让我举一个具体的例子。我想使用属性语法${x.y.z},但添加了elvis 运算符提供的“默认值”语法来处理${x.y.z}未定义的情况。

我尝试了以下语法但没有成功:

  • #{x.y.z?:'defaultValue'}
  • #{${x.y.z}?:'defaultValue'}

第一个给我

在“org.springframework.beans.factory.config.BeanExpressionContext”类型的对象上找不到字段或属性“x”

这表明 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"));
    }
}

挑战 - 在 bean 文件中提出一个 SpEL 表达式,它允许我在${x.y.z}无法解析的情况下指定默认值,并且此默认值必须指定为表达式的一部分,而不是在另一个属性集中外部化。

4

9 回答 9

27

要从 SpEL 表达式访问属性占位符,可以使用以下语法:#{'${x.y.z}'}. 但是,它无法解决您使用 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>
于 2010-01-11T12:36:23.483 回答
11

${myProps.item:defaultValue}表示当myProps.item不存在时,使用defaultValue. 这是属性占位符的默认行为。

#{defaultValue}表示文字值的 SpEL。

所以,${myProps.item:#{defaultValue}}表示什么时候myProps.item不存在,然后计算SpEL的值,并将其分配给目标字段。

例子:

${redis.auth:#{null}}表示当redis.auth属性不存在时,将其设置为null.

于 2015-04-13T10:37:37.350 回答
11

看来你错过了冒号:

#{ ${x.y.z} ?: 'defaultValue' }
于 2010-01-11T12:22:27.140 回答
8

如果您只想为占位符设置默认值,请参见

   <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"/>
于 2013-06-13T09:45:42.240 回答
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}的对象,该成员导致 NPE,因为不存在。abca

于 2012-03-14T21:54:06.000 回答
3

实际上 Property-Placeholder 可以自己解决您的问题。即您可以在 Spring 上下文中使用 property 显式指定默认设置properties。然后,您可以指定应该使用的设置的位置,并将属性设置localOverridetrue。在这种情况下,将在外部资源中找到的所有属性(在location属性中指定)将覆盖默认属性(在上下文中明确定义)。

希望我有所帮助。

于 2011-02-01T13:39:31.000 回答
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}' }"/> 
   ...
于 2016-07-06T07:05:45.807 回答
0

我已经尝试了以下方法并且它有效(虽然非常难看):

#{ myProps.getProperty('x.y.z')?:'Value B' }

于 2012-07-24T08:42:04.147 回答
0

无需使用 Elvis,只需在冒号后提供默认值即可。

@Value("${my.connection.timeout:5000}")
private int myTimeoutMillis;

或者

@Retryable(maxAttemptsExpression = "#{${my.max.attempts:10}}")
public void myRetryableMethod() {
    // ...
}
于 2018-10-09T15:58:37.470 回答