Présentation

Explication du Spring Expression Language

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com

A travers des articles précédents on a pu constater que certaines annotations utilisent des expressions un peu spéciales. Par exemple, dans le cas de l'implémentation du Spring Security dans notre application web, on a été amenés à vérifier si l'utilisateur effectue les opérations sur son compte. Pour mettre cette fonctionnalité en place, on a utilisé Spring Expression Language (SpEL), un langage d'expression fourni avec Spring.

Qu'est-ce que c'est Spring Expression Language ?

SpEL est un langage qui permet d'écrire les expressions. Elles sont ensuite évaluées par Spring et leurs résultats sont retournés à l'application. L'évaluation se fait au moment de l'exécution du code (runtime).

Les classes liées à cette fonctionnalité sont placées dans le package org.springframework.expression. Le traitement de l'expression se base sur les implémentations de l'interface ExpressionParser. Avec sa méthode parseExpression() on reçoit l'instance de l'interface Expression qui peut être employée dans la récupération de la valeur de l'expression parsée.

Regardons maintenant les fonctionnalités supportées par ces expressions (parser sera défini de cette manière ExpressionParser parser = new SpelExpressionParser();) :
- litérales : permet de récupérer les chaînes de caractères, les dates, les valeurs numériques , booléennes et null. La récupération de toutes les valeurs se fait par "". Si l'on souhaite retrouver un String, on doit utiliser l'expression "''", comme le démontre l'exemple :

String test = parser.parseExpression("'Test'").getValue(String.class);

- classes : grâce à l'opérateur T on peut spécifier une instance de la classe java.lang.Class :
Class subscriberClass = parser.parseExpression("T(library.model.entity.Subscriber)").getValue(Class.class);

- accès au propriétés : avec le parseur on peut accéder et manipuler les propriétés des classes. Imaginons un objet Subscriber à qui on veut ajouter un tiret comme préfixe :
String newLogin = parser.parseExpression("'-'.concat(#subscriber.login)").getValue(context, String.class);

- invocation des méthodes : les méthodes peuvent être appelées naturellement; il suffit juste de passer le nom de la méthode à appeler avec le paramètre exigé :
String newName = parser.parseExpression("getNewName('Mock')").getValue(contextSpel, String.class);

- opérations conditionnelles : les expressions permettent également d'effectuer les opérations de comparaison. Ces opérations peuvent être simples (==, >, <) aussi bien que compliquées (instanceof pour vérifier s'il s'agit de l'instance d'une classe ou matches pour s'assurer que la chaîne de caractères correspond à l'expression régulière définie). On peut également regrouper ces conditions avec les mots and (&&) et or (||). L'exécution des calculs mathématiques est également possible :
boolean isConfirmed = parser.parseExpression("#subscriber.confirmed == T(library.model.entity.Subscriber).IS_CONFIRMED").getValue(context, Boolean.class);

int result = parser.parseExpression("10 - #subscriber.getBookingNb()").getValue(context, Integer.class);

- définition des variables : avec la méthode setValue() on peut définir les valeurs d'une classe :
parser.parseExpression("globalIsConfirmed").setValue(contextSpel, true);

- appel des constructeurs : avec les expressions on peut également initialiser de nouveaux objets :
Subscriber subscriberInventor = parser.parseExpression("new library.model.entity.Subscriber()").getValue(Subscriber.class);

- référencement des beans : à travers un bean resolver et le symbole @, on peut aussi récupérer les beans depuis le contexte :
BeanResolver beanResolver = new BeanFactoryResolver(beanFactory);
context.setBeanResolver(beanResolver);
Object imageToolBean = parser.parseExpression("@imageTool").getValue(context);

- listes présentées en une seule ligne : on peut facilement, en une seule ligne et avec l'utilisation des {}, définir des listes :
List letters = (List) parser.parseExpression("{'a', 'b', 'c'}").getValue(context, List.class);

- if-then-else : les instructions if-else peuvent également être exécutées par SpEL :
boolean isConfirmedIfElse = parser.parseExpression("#subscriber.confirmed == 1 ? true : false").getValue(context, Boolean.class);

- variables : parmi les variables, on peut utiliser #this qui se réfère toujours à l'objet actuellement évalué. On peut également définir attribuer des valeurs à des variables d'une classe :
Subscriber subscriberContext = new Subscriber();
subscriberContext.setLogin("test09");
System.out.println("subscriberContext.login before modifierContext call is : " + subscriberContext.getLogin());
StandardEvaluationContext modifierContext = new StandardEvaluationContext(subscriberContext);
modifierContext.setVariable("login", "test3");
parser.parseExpression("login = #login").getValue(modifierContext);
System.out.println("subscriberContext.login after modifierContext call is : " + subscriberContext.getLogin());

- définition des méthodes de l'utilisateurs : avec la méthode registerFunction(String name, Method m) on peut enregistrer les méthodes définies par un utilisateur :
abstract class Tester {
    public static String returnTesterString() {
        return "testerString";
    }
}

try {
    context.registerFunction("returnTesterString", Tester.class.getDeclaredMethod("returnTesterString", new Class[] {}));
        } catch (Exception e) {
    System.out.println("An error occured on registering function returnTesterString : " + e.getMessage());
}
String testerString = parser.parseExpression("#returnTesterString()").getValue(context, String.class);

- sélection des collections : comme dans jQuery, on peut effectuer les sélections sur les collections. L'exemple ci-dessous va sélectionner l'utilisateur avec le login "test" :
List subscribersTest = (List) parser.parseExpression("#subscribersList.?[login == 'testLogin']").getValue(context);

- projection des collections : au lieu de récupérer toutes les instances Subscriber, on peut vouloir d'en récupérer uniquement leurs logins :
List logins = (List) parser.parseExpression("#subscribersList.![login]").getValue(context, List.class);

- expressions des tamplates : le placement des expressions à évaluer dans les templates est aussi possible :
String subscriberSentence = parser.parseExpression("TestEntity sentence is : #{T(library.test.model.TestEntity).getTestString()}", new TemplateParserContext()).getValue(String.class);

Dans les exemples ci-dessus on a souvent utilisé la méthode getValue(context). Qu'est-ce qui se cache derrière ce context ? En fait, il s'agit de l'implémentation de l'interface StandardEvaluationContext. Elle permet d'accéder et de manipuler les attributs définis dans le même contexte. Imaginons qu'on crée un String test = "test";. Pour pouvoir le manipuler, il faudra initialiser le contexte et le passer en paramètre dans la méthode getValue(), comme dans les exemples ci-dessus.

Bartosz KONIECZNY Spring Expression Language

Une question ? Une remarque ?

*

*

Un conseil JavaScript

Comment supprimer un élément du tableau JavaScript ?

PHP a sa méthode unset() pour supprimer un élément du tableau. JavaScript n'est guère plus compliqué. Pour supprimer n'importe quel élément il suffit d'utiliser l'opérateur delete :

delete myArray[key];
On peut également utiliser la méthode pop() qui va automatiquement supprimer le dernier élément de notre Array(). On peut également enlever le premier élément du tableau en utilisant shift().