【SpringBoot】官方表达式语言SPEL(Spring Expression Language)全方位学习

SpEL是什么

SpEL即Spring表达式语言(Spring Expression Language)。

SpEL表达式的默认格式为:#{expression}。SpEL表达式以“#”开头,表达式主体包围在花括号中。

  • 我们通常使用的属性取值表达式(也可称为属性占位符,格式${expression})不可以嵌套SpEL表达式。不过SpEL表达式可以嵌套属性取值表达式,如下:
#{
    
    ${
    
    someProperty} + 2}
//如果属性“someProperty”的值是2,这个表达式的值就是4。

springboot入门04 – 使用SpEL表达式

运算符

  • 算数运算符(Arithmetic):+, -, *, /, %, ^, div, mod
  • 关系运算符(Relational):<, >, ==, !=, <=, >=, lt, gt, eq, ne, le, ge
  • 逻辑运算符(Logical): and, or, not, &&,
  • 条件运算符(Conditional):?:
  • 正则运算符(Regex)

接下来我们主要以@Value注解的形式介绍并演示这些运算符在SpEL中的应用。

算数运算符(Arithmetic)

    @Value("#{19 + 1}") // 20
    private double add;

    @Value("#{'String1 ' + 'string2'}") // "String1 string2"
    private String addString;

    @Value("#{20 - 1}") // 19
    private double subtract;

    @Value("#{10 * 2}") // 20
    private double multiply;

    @Value("#{36 / 2}") // 18
    private double divide;

    @Value("#{36 div 2}") // 18, the same as for / operator
    private double divideAlphabetic;

    @Value("#{37 % 10}") // 7
    private double modulo;

    @Value("#{37 mod 10}") // 7, the same as for % operator
    private double moduloAlphabetic;

    @Value("#{2 ^ 9}") // 512
    private double powerOf;

    @Value("#{(2 + 2) * 2 + 9}") // 17
    private double brackets;

除运算 和 取模运算都有字母形式的别名(除运算“div”,取模运算“mod”)。“+”运算符还可以用来执行字符串连接。

关系运算符(Relational)

    private boolean equal;
 
    @Value("#{1 eq 1}") // true
    private boolean equalAlphabetic;
 
    @Value("#{1 != 1}") // false
    private boolean notEqual;
 
    @Value("#{1 ne 1}") // false
    private boolean notEqualAlphabetic;
 
    @Value("#{1 < 1}") // false
    private boolean lessThan;
 
    @Value("#{1 lt 1}") // false
    private boolean lessThanAlphabetic;
 
    @Value("#{1 <= 1}") // true
    private boolean lessThanOrEqual;
 
    @Value("#{1 le 1}") // true
    private boolean lessThanOrEqualAlphabetic;
 
    @Value("#{1 > 1}") // false
    private boolean greaterThan;
 
    @Value("#{1 gt 1}") // false
    private boolean greaterThanAlphabetic;
 
    @Value("#{1 >= 1}") // true
    private boolean greaterThanOrEqual;
 
    @Value("#{1 ge 1}") // true
    private boolean greaterThanOrEqualAlphabetic;

所有的关系运算都有字母别名。主要是为了适配使用xml配置文件的场景。在xml中使用带有三角符号的运算符(如小于“<”,大于“>”等)是不被允许的,此时我们可以使用字母形式的别名(lt,gt等)来进行运算。

逻辑运算符(Logical)

    @Value("#{250 > 200 && 200 < 4000}") // true
    private boolean and;
 
    @Value("#{250 > 200 and 200 < 4000}") // true
    private boolean andAlphabetic;
 
    @Value("#{400 > 300 || 150 < 100}") // true
    private boolean or;
 
    @Value("#{400 > 300 or 150 < 100}") // true
    private boolean orAlphabetic;
 
    @Value("#{!true}") // false
    private boolean not;
 
    @Value("#{not true}") // false
    private boolean notAlphabetic;

同关系运算符一样,每个逻辑运算符也都有字母别名

条件运算符(Conditional)

条件运算符,顾名思义是用来根据不同的情况来注入不同的值。实际上,就是一个三目运算符:

    @Value("#{2 > 1 ? 'a' : 'b'}") // "a"
    private String ternary;

三目运算符主要被用来处理“if-then-else”这样的判定。

扫描二维码关注公众号,回复: 16499702 查看本文章
  • 通常的使用场景式是判断一个属性是否为null,如果是的话就返回一个默认值,如下:
    @Value("#{worker.name != null ? worker.name : 'zhyea'}")
    private String ternaryForNull;

此外还有一种“Elvis”运算符,简化了上面这种“判定是否为空,为空则返回默认值”的场景:

    @Value("#{worker.name ?: 'zhyea'}")
    private String elvis;

如上,“Elvis”运算符的符号是“?:”,我们可以在Groovy语言中见到它。现在SpEL也引入了这个运算符。

正则运算符(Regex)

正则运算符被用来校验字符串是否匹配某个指定的正则表达式。如下:

    @Value("#{'100' matches '\\d+' }") // true
    private boolean validNumericStringResult;
 
    @Value("#{'100fghdjf' matches '\\d+' }") // false
    private boolean invalidNumericStringResult;
 
    @Value("#{'valid alphabetic string' matches '[a-zA-Z\\s]+' }") // true
    private boolean validAlphabeticStringResult;
 
    @Value("#{'invalid alphabetic string #$1' matches '[a-zA-Z\\s]+' }") // false
    private boolean invalidAlphabeticStringResult;
 
    @Value("#{worker.id matches '\\d+'}") // true if someValue contains only digits
    private boolean validNumericValue;

访问List或Map对象

使用SpEL,我们还可以访问容器中的任何Map或List对象。看个例子:

如:们创建了一个List对象来存放工人名字,一个Map对象来存放每个工人的工资。

@Component("workersHolder")
public class WorkersHolder {
    
    
    
    private List<String> workers = new LinkedList<>();
    
    private Map<String, Integer> salaryByWorkers = new HashMap<>();
 
    public WorkersHolder() {
    
    
        workers.add("John");
        workers.add("Susie");
        workers.add("Alex");
        workers.add("George");
 
        salaryByWorkers.put("John", 35000);
        salaryByWorkers.put("Susie", 47000);
        salaryByWorkers.put("Alex", 12000);
        salaryByWorkers.put("George", 14000);
    }
 
    // getters & setters ...
}

使用SpEL来访问这两个集合对象里面的元素:

   @Value("#{workersHolder.salaryByWorkers['John']}") // 35000
    private Integer johnSalary;
 
    @Value("#{workersHolder.salaryByWorkers['George']}") // 14000
    private Integer georgeSalary;
 
    @Value("#{workersHolder.salaryByWorkers['Susie']}") // 47000
    private Integer susieSalary;
 
    @Value("#{workersHolder.workers[0]}") // John
    private String firstWorker;
 
    @Value("#{workersHolder.workers[3]}") // George
    private String lastWorker;
 
    @Value("#{workersHolder.workers.size()}") // 4
    private Integer numberOfWorkers;

使用代码解析SpEL表达式

有时候会需要编程处理SpEL表达式。Spring也为我们提供了相关的工具类。这些类都位于“spring-expression”包下。

下面是一个封装好的解析SpEL表达式的方法:

public static <T> T parse(String expr) {
    
    
    ExpressionParser expressionParser = new SpelExpressionParser();
    Expression expression = expressionParser.parseExpression(expr);
    return (T) expression.getValue();
}

调用ExpressionParser.parseExpression()后获得的值是Object类型的,这里会通过强制类型转换转为需要的类型。

  • 现在我们将字符串(’zhyea’)作为SpEL表达式传入并执行,毫无疑问,执行结果也应当返回字符串“zhyea”:
    String expr = "'zhyea'";
    String r = parse(expr);
    Assert.assertEquals("zhyea", r);
    

在SpEL中还可以调用方法,访问属性,使用构造器。代码大致如下:

// call method length()
String expr = "'zhyea'.length()";
int l = parse(expr);
        
// call constructor
String expr = "new String('chobit').length()";
int l = parse(expr);
        
// visit properties
String expr = "'zhyea'.bytes";
byte[] r = parse(expr);

之前我们获取解析后的值是通过了一次强制类型转换的。Spring也提供了一个传入泛型类型来获取目标类型结果的方法,简单做了下封装:

public static <T> T parse(String expr, Class<T> clazz) {
    
    
    ExpressionParser expressionParser = new SpelExpressionParser();
    Expression expression = expressionParser.parseExpression(expr);
    return expression.getValue(clazz);
}

变量与赋值

变量:在表达式中使用语法#变量名引用

ExpressionParser ep= new SpelExpressionParser();
//创建上下文变量
EvaluationContext ctx = new StandardEvaluationContext();
 //在上下文中设置变量,变量名为name,内容为Hello
ctx.setVariable("name", "Hello");
System.out.println(ep.parseExpression("#name").getValue(ctx));//输出:Hello

赋值:属性设置是通过使用赋值运算符。这通常是在调用setValue中执行但也可以在调用getValue内,也可通过”#varName=value”的形式给变量赋值。

System.out.println(ep.parseExpression("#name='Ryo'").getValue(ctx));//输出:Ryo

猜你喜欢

转载自blog.csdn.net/qq877728715/article/details/108126691