【问题标题】:How does Spring 3 expression language interact with property placeholders?Spring 3 表达式语言如何与属性占位符交互?
【发布时间】:2010-01-11 11:45:25
【问题描述】:

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

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

【问题讨论】:

  • 你把表达式放在哪里?在代码中,或在您的 xml / @Value / 等中。
  • 在 bean 定义 XML 中,例如&lt;property name="prop" ref="&lt;exp&gt;"/&gt;
  • 不确定它是否合适,但是 - 你试过 value= 而不是 ref= 吗?
  • 我没有尝试过,不,但它是我试图解决的 bean 引用,而不是一个值。 ref="${x.y.z}" 适用于 Spring 2.5。

标签: java spring spring-el


【解决方案1】:

要从 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>

【讨论】:

  • 感谢您的建议,但我需要能够使用已经在上下文中的 existing 属性占位符配置器,我真的不能摆弄它。此外,我需要能够在线指定默认值,而不是将它们外部化。
【解决方案2】:

看来你漏掉了冒号:

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

【讨论】:

  • 呃,你是对的......编辑了我的问题......它没有解决它,但至少现在我得到了一个更有用的例外......
【解决方案3】:

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

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

所以,${myProps.item:#{defaultValue}}表示当myProps.item不存在时,计算SpEL的值,并赋值给目标字段。

例子:

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

【讨论】:

  • 您好,虽然这可能很好地回答了这个问题,但请注意,其他用户可能不像您那样知识渊博。您为什么不添加一些关于此代码为何有效的解释?谢谢!
【解决方案4】:

如果您只想为占位符设置默认值,请参阅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"/>

【讨论】:

    【解决方案5】:

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

    希望我能帮上忙。

    【讨论】:

      【解决方案6】:

      您需要添加它以使其在您的示例中运行

      <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} 评估为具有成员 b 和成员 c 的对象 a,这会导致 NPE,因为 a 不存在。

      【讨论】:

        【解决方案7】:

        你可以:

        <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}' }"/> 
           ...
        

        【讨论】:

          【解决方案8】:

          我尝试了以下方法并且效果很好(虽然很丑):

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

          【讨论】:

            【解决方案9】:

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

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

            @Retryable(maxAttemptsExpression = "#{${my.max.attempts:10}}")
            public void myRetryableMethod() {
                // ...
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-03-26
              • 1970-01-01
              • 1970-01-01
              • 2013-08-18
              • 2019-03-17
              • 2014-07-16
              • 1970-01-01
              相关资源
              最近更新 更多