[In-depth explanation of Spring principles and actual combat] "EL Expression Development Series" in-depth analysis of SpringEL expression theory and practical application

Introduction

Spring3 introduced the Spring Expression Language (SpEL) as a powerful and concise Bean assembly method. It can dynamically inject values ​​into our properties or constructors through expressions executed at runtime, and supports calling static constants provided by JDK and obtaining configurations in external Properties files. Let's dive into the magic of SpEL! The static constant provided in K obtains the configuration in the external Properties file.

usage

Parser for Spring EL expressions—SpelExpressionParser

ExpressionParser parser = new SpelExpressionParser();

SpEL's text expression support

SpEL's text expressions support a variety of types, including strings (need to be declared in single quotes), dates, numbers, Boolean types, and null. For numeric types, SpEL supports negative numbers, exponents, and decimals, and uses Double.parseDouble() by default for expression type conversion.

parser.parseExpression("'hello'").getValue(String.class); // hello , 注意单引号
parser.parseExpression("1.024E+3").getValue(Long.class);  // 1024  , 指数形式
parser.parseExpression("0xFFFF").getValue(Integer.class); // 65535 , 十六进制
parser.parseExpression("true").getValue(Boolean.class);   // true
parser.parseExpression("null").getValue();    

SpEL's processing mode for variables

// 定义变量
String name = "Tom";
EvaluationContext context = new StandardEvaluationContext();  //表达式的上下文,
context.setVariable("myName", name);                          //为了让表达式可以访问该对象, 先把对象放到上下文中
ExpressionParser parser = new SpelExpressionParser();
// 访问变量
parser.parseExpression("#myName").getValue(context, String.class);   // Tom , 使用变量
// 直接使用构造方法创建对象
parser.parseExpression("new String('aaa')").getValue(String.class);   // aaa

SpEL attribute access and method call

In SpEL, properties can be accessed directly using their names, case insensitive. For arrays and lists, they can be accessed by subscripting (eg list[index]). For the Map type, its key value can be used as an index (such as map[key]) for access, which is very convenient. At the same time, the first letter of the attribute name can be uppercase or lowercase, and only the first letter is case-insensitive.

method can directly access

// 准备工作
EvaluationContext context = new StandardEvaluationContext();  // 表达式的上下文,
ExpressionParser parser = new SpelExpressionParser();

Object attribute access control

It is mainly for object-oriented attributes to control access operation processing.

// 一个普通的POJO
EvaluationContext context = new StandardEvaluationContext();  // 表达式的上下文,
context.setVariable("person", person);                        // 为了让表达式可以访问该对象, 先把对象放到上下文中
ExpressionParser parser = new SpelExpressionParser();
Person person = new Person("Tom", 18); 
// Tom , 属性访问
parser.parseExpression("#person.name").getValue(context, String.class); 
// Tom , 属性访问, 但是首字母大写了      
parser.parseExpression("#person.Name").getValue(context, String.class);  

Collection/Object Access

First of all, for the operation access of collection type

EvaluationContext context = new StandardEvaluationContext();  // 表达式的上下文,
List<String> list = Lists.newArrayList("a", "b");
Map<String, String> map = Maps.newHashMap();
map.put("A", "1");
map.put("B", "2");
context.setVariable("person", person);                        // 为了让表达式可以访问该对象, 先把对象放到上下文中
context.setVariable("map", map);
context.setVariable("list", list);
// 列表
parser.parseExpression("#list[0]").getValue(context, String.class)           // a , 下标
// map
parser.parseExpression("#map[A]").getValue(context, String.class);           // 1 , key
// 方法
parser.parseExpression("#person.getAge()").getValue(context, Integer.class); // 18 , 方法访问 

SpEL's manipulation and control of types and native classes

The T operator can be used to obtain type information and call static methods of objects.

// 获取类型
parser.parseExpression("T(java.util.Date)").getValue(Class.class); // class java.util.Date
// 访问静态成员(方法或属性)
parser.parseExpression("T(Math).abs(-1)").getValue(Integer.class); // 1
// 判断类型
parser.parseExpression("'asdf' instanceof T(String)").getValue(Boolean.class); // true;

operator

Spring EL supports most mathematical, logical and relational operators.

  • Relational operators, including: eq(==), ne(!=), lt()<, le(<=), gt(>), ge(>=)
  • Logical operators, including: and(&&), or(||), not(!)
  • Mathematical operators, including: addition (+), subtraction (-), multiplication (*), division (/), modulo (%), exponentiation (^)
  • 其他操作符, 如: 三元操作符, instanceof, 赋值(=), 正则匹配

另外三元操作符有个特殊的用法, 一般用于赋默认值, 比如: parseExpression("#name?:'defaultName'"), 如果变量name为空时设置默认值.

parser.parseExpression("1 > -1").getValue(Boolean.class);         // true
parser.parseExpression("1 gt -1").getValue(Boolean.class);        // true
parser.parseExpression("true or true").getValue(Boolean.class);   // true
parser.parseExpression("true || true").getValue(Boolean.class);   // true
parser.parseExpression("2 ^ 3").getValue(Integer.class);          // 8
parser.parseExpression("true ? true : false").getValue(Boolean.class); // true
parser.parseExpression("#name ?: 'default'").getValue(context, String.class); // default
parser.parseExpression("1 instanceof T(Integer)").getValue(Boolean.class); // true
parser.parseExpression("'5.00' matches '^-?\d+(\.\d{2})?$'").getValue(Boolean.class); // true
parser.parseExpression("#person.name").getValue(context, String.class);  // Tom , 原来的值
parser.parseExpression("#person.name = 'Jim'").getValue(context, String.class); // Jim , 赋值之后
parser.parseExpression("#person.name").getValue(context, String.class);  // Jim, 赋值起了作用

避免空指针

当访问一个对象的属性或方法时, 若该对象为null, 就会出现空指针异常. 安全导航会判断对象是否为null,如果是的话, 就返回null而不是抛出空指针异常. 使用方式就是在对象后面加个 ?, 如下:

// 使用这种表达式可以避免抛出空指针异常
parser.parseExpression("#name?.toUpperCase()").getValue(context, String.class); // null

this变量

有个特殊的变量#this来表示当前的对象. 常用于集合的过滤

// this 使用示例
parser.parseExpression("{1, 3, 5, 7}.?[#this > 3]").getValue(); // [5, 7]

集合选择

可以使用选择表达式对集合进行过滤或一些操作,从而生成一个新的符合选择条件的集合, 有如下一些形式:

  • ?[expression]: 选择符合条件的元素
  • ^[expression]: 选择符合条件的第一个元素
  • $[expression]: 选择符合条件的最后一个元素
  • ![expression]: 可对集合中的元素挨个进行处理

对于集合可以配合#this变量进行过滤, 对于map, 可分别对keySet及valueSet分别使用key和value关键字;

// 集合
parser.parseExpression("{1, 3, 5, 7}.?[#this > 3]").getValue(); // [5, 7] , 选择元素
parser.parseExpression("{1, 3, 5, 7}.^[#this > 3]").getValue(); // 5 , 第一个
parser.parseExpression("{1, 3, 5, 7}.$[#this > 3]").getValue(); // 7 , 最后一个
parser.parseExpression("{1, 3, 5, 7}.![#this + 1]").getValue(); // [2, 4, 6, 8] ,每个元素都加1
// map
Map<Integer, String> map = Maps.newHashMap();
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
map.put(4, "D");
EvaluationContext context = new StandardEvaluationContext();
context.setVariable("map", map);
parser.parseExpression("#map.?[key > 3]").getValue(context);             // {4=D}
parser.parseExpression("#map.?[value == 'A']").getValue(context);        // {1=A}
parser.parseExpression("#map.?[key > 2 and key < 4]").getValue(context); // {3=C}

模板表达式

模板表达式允许文字和表达式混合使用, 一般选择使用#{}作为一个定界符:

EvaluationContext context = new StandardEvaluationContext();  //表达式的上下文,
ExpressionParser parser = new SpelExpressionParser();
parser.parseExpression("他的名字为#{#person.name}", new TemplateParserContext()).getValue(context); // 他的名字为Tom

Guess you like

Origin juejin.im/post/7233700714072178747