Java Spring 3 表达式语言如何与属性占位符交互?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2041558/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 02:53:51  来源:igfitidea点击:

How does Spring 3 expression language interact with property placeholders?

javaspringspring-el

提问by skaffman

Spring 3 has introduced a new expression language(SpEL) which can be used in bean definitions. The syntax itself is fairly well specified.

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

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?

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

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 operatorto handle cases where ${x.y.z}is undefined.

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

I've tried the following syntaxes without success:

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

  • #{x.y.z?:'defaultValue'}
  • #{${x.y.z}?:'defaultValue'}
  • #{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'

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

which suggests that SpEL doesn't recognise this as a property placeholder.

这表明 SpEL 不会将此识别为属性占位符。

The second syntax throws an exception saying that the placeholder is not recognised, so the placeholder resolver isbeing 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:

首先,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>

Then, the trivial bean class:

然后,琐碎的bean类:

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 mustbe specified as part of the expression, not externalized in another property set.

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

回答by Bozho

It seems you missed the colon:

看来你错过了冒号:

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

回答by axtavt

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.

从使用SpEL表达访问属性占位符,下面的语法可以使用:#{'${x.y.z}'}。但是,它无法解决您使用 elvis 运算符和默认值的问题,因为它${x.y.z}在无法解决时会引发异常。

But you don't need SpEL to declare default values for properties:

但是您不需要 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>

回答by Myron

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 property localOverrideto true. In such case all properties that will be found in external resources (specified in locationproperty) will override default ones (explicitly defined within context).

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

Hope I helped.

希望我有所帮助。

回答by micfra

You need to add this to get it running within your example

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

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

Your approach does not work, because Spring tries to evaluate ${a.b.c}to an object awith member bwith member cwhich results in a NPE because adoes not exist.

你的方法是行不通的,因为春天试图评估${a.b.c}一个对象a与成员b与成员c这是因为导致NPEa不存在。

回答by Gergely Toth

I've tried the following and it worked (pretty ugly though):

我尝试了以下方法并且它有效(虽然很丑陋):

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

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

回答by btpka3

If you just want to set default value for placeholder, see this:

如果你只是想为占位符设置默认值,看到这个

   <property name="value" value="${x.y.z:defaultValue}"/> 

If you want to test interaction between with SpEL and placeholder, using this:

如果要测试 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"/>

回答by smartwjw

${myProps.item:defaultValue}means that when myProps.itemdoes not exists, use defaultValue. That's the default behaviour of property placeholder.

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

#{defaultValue}means SpEL for literal value.

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

So,${myProps.item:#{defaultValue}}means when myProps.itemdoes not exists, then calculate the value of SpEL, and assign it to target field.

所以,${myProps.item:#{defaultValue}}意味着当myProps.item不存在时,则计算SpEL的值,并将其分配给目标字段。

Example:

例子:

${redis.auth:#{null}}means when redis.authproperties does not exists, set it to null.

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

回答by qxo

you may:

你可以:

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

or

或者

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

or ...

或者 ...

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

回答by whistling_marmot

There's no need to use Elvis, just supply the default after a colon.

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

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

or

或者

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