Spring Expression Language (SpEL)

SpEL est Le langage d’expression spécifique à Spring Framework. Il est utilisé à plusieurs endroits, comme dans l’annotation @Value ou avec les annotations de Spring Security

Exemples

Dans ce premier exemple, on récupère la clé d’API du service mail. Il a été enregistré sous forme de propriété dans le fichier application.yml.

@Configuration
public class EmailConfig {

  @Value("${application.email.api-key}")
  private String apiKey;

  ...
}

Dans ce deuxième exemple, on réstreint l’accès à une méthode aux utilisateurs qui ont le rôle d’administrateur.

@Service
public class ProductService {

  @PreAuthorize("hasRole('ROLE_ADMIN')")
  private void create(Product product) {
    ...
  }

  ...
}

Syntaxe

Pour la syntaxe complète, il faut aller voir la documentation de référence du langage. Une bonne partie de la syntaxe ressemble à Java, avec quelques spécificités.

  • Expression simple

  • Classe: T(java.lang.Math)

  • Paramètres (ou variable): #product, avec 2 variables spécifiques: #this et #root

  • Beans: @productService s’il y a un bean resolver, et &productService pour s’adresser directement au beanFactory, comme pour les propriétés

  • Propriétés: ${application.email.api-key}

  • Expressions mixtes, c’est à dire une expression dans du texte statique: "random number is #{T(java.lang.Math).random()}"

  • Fonctions et méthodes

Résolution d’expression

On se préoccupe rarement de l’API de résolution puisque l’usage le plus courant est dans les annotations. Dans certains cas, on peut avoir besoin de le faire par programmation.

Expression simple

String exp = "0.1 + T(java.lang.Math).random()";

ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(exp);

Double value = expression.getValue(Double.class);

Expression avec paramètre

String exp = "new java.util.Random().nextInt(#bound)";

ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(exp);

StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("bound", 100);

Integer value = expression.getValue(context, Integer.class);

Expression avec beans

Si l’expression fait référence à des beans, ceux-ci doivent être résolus.

String exp = "@randomService.nextInt(#bound)";

ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(exp);

StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("bound", 100);
context.setBeanResolver(new BeanFactoryResolver(beanFactory));

Integer value = expression.getValue(context, Integer.class);

Expression de sécurité

Les annotations de sécurité comme @PreAuthorize ou @PostFilter utilisent des méthodes comme isAuthenticated(), isAnonymous() ou hasRole(…​). Ces méthodes sont dans la classe SecurityExpressionRoot qu’il faut donc utiliser comme racine du parser.

String exp = "hasRole('ROLE_ADMIN')";

ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(exp);

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
SecurityExpressionRoot securityRoot = new SecurityExpressionRoot(authentication) {};

Boolean value = expression.getValue(securityRoot, Boolean.class);

Pour les exemples complets, on peut passer un contexte et une racine.

Boolean value = expression.getValue(context, securityRoot, Boolean.class);