Spring 3では、Bean定義で使用できる新しい expression language (SpEL)が導入されました。構文自体はかなり適切に指定されています。
明確ではないのは、SpELが以前のバージョンに既に存在していたプロパティプレースホルダー構文とどのように相互作用するかです。 SpELはプロパティプレースホルダーをサポートしていますか、または両方のメカニズムの構文を組み合わせて、組み合わせることを望んでいますか?
具体例を挙げましょう。プロパティ構文${x.y.z}
を使用したいが、${x.y.z}
が未定義の場合を処理するために elvis operator によって提供される「デフォルト値」構文を追加したい。
私は成功せずに次の構文を試しました:
#{x.y.z?:'defaultValue'}
#{${x.y.z}?:'defaultValue'}
最初のものは私に与えます
タイプ 'org.springframework.beans.factory.config.BeanExpressionContext'のオブジェクトでフィールドまたはプロパティ 'x'が見つかりません
これは、SpELがこれをプロパティプレースホルダーとして認識しないことを示唆しています。
2番目の構文は、プレースホルダーが認識されないという例外をスローするため、プレースホルダーリゾルバーisが呼び出されますが、プロパティが定義されていないため、期待どおりに失敗します。
ドキュメントはこの相互作用について言及していないため、そのようなことは不可能であるか、文書化されていません。
誰でもこれを行うことができましたか?
わかりました、私はこれのための小さい、自己完結型のテストケースを思いつきました。これはすべてそのまま機能します:
まず、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"));
}
}
チャレンジ-${x.y.z}
を解決できない場合にデフォルト値を指定できるようにするBeanファイルでSpEL式を考え出すこと、およびこのデフォルトmustを一部として指定する別のプロパティセットで外部化されていない式。
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>
コロンを見逃したようです:
#{ ${x.y.z} ?: 'defaultValue' }
${myProps.item:defaultValue}
は、myProps.item
は存在しません。defaultValue
を使用してください。これがプロパティプレースホルダーのデフォルトの動作です。
#{defaultValue}
は、リテラル値のSpELを意味します。
そう、${myProps.item:#{defaultValue}}
は、myProps.item
が存在しない場合は、SpELの値を計算し、ターゲットフィールドに割り当てます。
例:
${redis.auth:#{null}}
は、redis.auth
プロパティが存在しないため、null
に設定します。
プレースホルダーのデフォルト値を設定する場合は、 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"/>
サンプル内で実行するには、これを追加する必要があります
<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
を評価しようとしますが、a
が存在しないためNPEになるため、アプローチは機能しません。
実際、Property-Placeholderは問題自体を解決できます。つまりプロパティproperties
を使用して、Springコンテキストでデフォルト設定を明示的に指定できます。次に、使用する設定の場所を指定し、プロパティlocalOverride
をtrue
に設定します。そのような場合、外部リソース(location
プロパティで指定)で検出されるすべてのプロパティは、デフォルトのプロパティ(コンテキスト内で明示的に定義)をオーバーライドします。
お役に立てば幸いです。
してもいいです:
<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}' }"/>
...
Elvisを使用する必要はありません。コロンの後にデフォルトを指定するだけです。
@Value("${my.connection.timeout:5000}")
private int myTimeoutMillis;
または
@Retryable(maxAttemptsExpression = "#{${my.max.attempts:10}}")
public void myRetryableMethod() {
// ...
}
私は次を試してみましたが、うまくいきました(かなりいです):
#{ myProps.getProperty('x.y.z')?:'Value B' }