web-dev-qa-db-fra.com

Comment le langage d'expression Spring 3 interagit-il avec des espaces réservés de propriétés?

Spring 3 a introduit un nouveau expression language (SpEL) qui peut être utilisé dans les définitions de bean. La syntaxe elle-même est assez bien spécifiée.

Ce qui n'est pas clair, c'est comment, le cas échéant, SpEL interagit avec la syntaxe de marque de réservation de propriété qui était déjà présente dans les versions précédentes. Est-ce que SpEL prend en charge les espaces réservés de propriété ou dois-je combiner la syntaxe des deux mécanismes et espérer qu'ils se combinent?

Laissez-moi vous donner un exemple concret. Je souhaite utiliser la propriété syntaxe ${x.y.z}, mais avec l’ajout de la syntaxe "valeur par défaut" fournie par l’opérateur elvis pour gérer les cas dans lesquels ${x.y.z} est indéfini.

J'ai essayé les syntaxes suivantes sans succès:

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

Le premier me donne 

Le champ ou la propriété 'x' est introuvable sur objet de type 'org.springframework.beans.factory.config.BeanExpressionContext'

ce qui suggère que SpEL ne reconnaît pas cela comme un espace réservé de propriété.

La deuxième syntaxe génère une exception indiquant que l'espace réservé n'est pas reconnu. Le résolveur d'espace réservé est appelé, mais échoue comme prévu, car la propriété n'est pas définie.

Les docs ne font aucune mention de cette interaction, donc ce n'est pas possible ou ce n'est pas documenté.

Quelqu'un a réussi à faire ça?


OK, je suis venu avec un petit cas de test autonome pour cela. Tout cela fonctionne tel quel:

Tout d'abord, les définitions de haricot:

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

Ensuite, la classe de haricots triviale:

test de l'emballage;

public class Bean {

    String value;

    public void setValue(String value) {
        this.value = value;
    }
}

Et enfin, le cas de test:

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

Le défi - trouver une expression SpEL dans le fichier beans qui me permette de spécifier une valeur par défaut dans les cas où ${x.y.z} ne peut pas être résolu, et cette valeur par défaut doit être spécifiée dans le cadre de l'expression, non externalisée dans un autre ensemble de propriétés.

52
skaffman

Pour accéder à un espace réservé de propriété à partir d'une expression SpEL, la syntaxe suivante peut être utilisée: #{'${x.y.z}'}. Cependant, il ne peut pas résoudre votre problème avec l'opérateur elvis et les valeurs par défaut, car il lèverait une exception lorsque ${x.y.z} ne pourrait pas être résolu.

Mais vous n'avez pas besoin de SpEL pour déclarer les valeurs par défaut des propriétés:

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

Il semble que vous ayez raté le côlon:

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

Si vous voulez juste définir la valeur par défaut pour un espace réservé, voir this :

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

Si vous souhaitez tester l’interaction entre SpEL et l’espace réservé, procédez comme suit:

   <!-- 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"/>
8
btpka3

${myProps.item:defaultValue} signifie que lorsque myProps.item n'existe pas, utilisez defaultValue. C'est le comportement par défaut de l'espace réservé de propriété. 

#{defaultValue} signifie SpEL pour valeur littérale. 

Donc, ${myProps.item:#{defaultValue}} signifie que lorsque myProps.item n'existe pas, calculez la valeur de SpEL et affectez-la au champ cible.

Exemple:

${redis.auth:#{null}} signifie que lorsque les propriétés redis.auth n'existent pas, définissez-le sur null.

7
smartwjw

Vous devez ajouter ceci pour le faire fonctionner dans votre exemple

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

Votre approche ne fonctionne pas car Spring tente d'évaluer ${a.b.c} avec un objet a avec le membre b avec le membre c, ce qui donne un NPE car a n'existe pas.

3
micfra

En réalité, Property-Placeholder peut résoudre vos problèmes par vous-même. vous pouvez spécifier explicitement les paramètres par défaut dans le contexte Spring, à l'aide de la propriété properties. Vous pouvez ensuite spécifier l'emplacement des paramètres à utiliser et définir la propriété localOverride sur true. Dans ce cas, toutes les propriétés qui seront trouvées dans les ressources externes (spécifiées dans la propriété location) remplaceront celles par défaut (explicitement définies dans le contexte).

J'espère que j'ai aidé.

3
Myron

tu peux:

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

ou

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

ou ...

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

Il n'est pas nécessaire d'utiliser Elvis, il suffit de fournir la valeur par défaut après un colon.

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

ou

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

J'ai essayé ce qui suit et cela a fonctionné (assez moche cependant):

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

0
Gergely Toth