Spring Expression Language(简称 SpEL)是一种功能强大的表达式语言,支持运行时查询和操作对象图。

Java 有许多可用的表达式语言,例如 JSP EL,OGNL,MVEL 和 JBoss EL,SpEL 语法类似于 JSP EL,功能类似于 Struts2 中的 OGNL,能在运行时构建复杂表达式、存取对象图属性、调用对象方法等,并且能与 Spring 功能完美整合,如 SpEL 可以用来配置 Bean 定义。

SpEL 并不与 Spring 直接相关,可以被独立使用。SpEL 表达式的创建是为了向 Spring 社区提供一种受良好支持的表达式语言,该语言适用于 Spring 家族中的所有产品。也就是说,SpEL 是一种与技术无关的 API,可以集成其它表达式语言。

1. SpEL 的应用场景

    1) 使用 SpEL 提供的接口和类:

        Expression interface:该接口负责评估表达式字符串;
        ExpressionParser interface:该接口负责解析字符串;
        EvaluationContext interface:该接口负责定义上下文环境;

    2) Spring XML配置

        SpEL 表达式可以与 XML 或基于注解的配置元数据一起使用。

        可以使用以下表达式来设置属性或构造函数的参数值:

          

<bean id="number" class="com.example.Number">
                <property name="randomNumber" value="#{T(java.lang.Math).random() * 100.0}"/>
            </bean>



        也可以通过名称引用其它 Bean 方法,如以下代码:

           

<bean id="shape" class="com.example.Shape">
                <property name="num" value="#{number.getRandomNumber()}"/>
            </bean>


    3) 注解的配置

        @Value 注解可以放在字段、方法、以及构造函数参数上,以指定默认值。

       

class SpelTest {

            @Value("#{{1, 2, 3}}")
            private int[] array;
            @Value("#{'\"Java\",\"Spring\",\"Idea\"'.split(',')}")
            private List<String> list;
            @Value("#{'\"Tree\",\"Dog\",\"River\"'.split(',')}")
            private Set<String> set;
            @Value("#{{\"name\":\"Tester\",\"age\": 21}}")
            private Map<String, String> map;

            ...

        }


     

2. 表达式语法

    1) 引用 Bean 属性和方法

       

<!-- 引用其他对象的属性 -->
        <property name="randomNumber" value="#{number.randomNumber}" />
        <!-- 引用其他对象的方法 -->
        <property name="randomNumber" value="#{number.getRandomNumber()}" />

        *注:属性和方法的访问权限设置为可访问 (public, default 或 protected
        )



    2) 运算符

        算术运算符:+,-,*,/,%,^

          

<!-- 1000 -->
            <property name="width" value="#{10^3}" />
            <!-- 2021年1月 -->
            <property name="date" value="#{2020+'年'+01+'月'}" />

        比较运算符:<,>,==,<=,>=,lt,gt,eq,le,ge
        逻辑运算符:and,or,not,&&,||,!
        三目运算符:?true:false
        正则表达式:matches

            <!-- true -->
            <property name="isEmail" value="#{'test@123.com' matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}'}" />



    3) 调用静态方法或静态属性

        通过 T() 调用一个类的静态方法,它将返回一个 Class Object,然后再调用相应的方法或属性:

      

<!-- 3.141592653589793 -->
        <property name="PI" value="#{T(java.lang.Math).PI}" />



    4) 集合定义

        使用 “{表达式, ...} ” 定义。

        Array, List, Set: {1,2,3}

        Map: {"name":"Tester", "age":21}

3. 实例

    Spring 配置文件(spring-beans.xml)配置格式如下:

1         <?xml version="1.0" encoding="UTF-8"?>
 2         <beans xmlns="http://www.springframework.org/schema/beans"
 3                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4                xmlns:aop="http://www.springframework.org/schema/aop"
 5                xmlns:context="http://www.springframework.org/schema/context"
 6                xsi:schemaLocation="http://www.springframework.org/schema/beans
 7                                    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
 8                                    http://www.springframework.org/schema/context
 9                                    http://www.springframework.org/schema/context/spring-context-4.0.xsd
10                                    http://www.springframework.org/schema/aop
11                                    http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
12 
13             <context:component-scan base-package="com.example"/>
14 
15             <bean id="number" class="com.example.Number">
16                 <property name="randomNumber" value="#{T(java.lang.Math).random() * 100.0}"/>
17             </bean>
18 
19             <bean id="shape" class="com.example.Shape">
20                 <property name="num" value="#{number.getRandomNumber()}"/>
21                 <property name="width" value="#{10^3}" />
22                 <property name="date" value="#{2020+'年'+1+'月'}" />
23                 <property name="isEmail" value="#{'test@123.com' matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}'}" />
24                 <property name="PI" value="#{T(java.lang.Math).PI}" />
25             </bean>
26 
27         </beans>

    代码:

1         package com.example;
  2 
  3         import java.util.List;
  4         import java.util.Map;
  5         import java.util.Set;
  6         import java.util.Arrays;
  7 
  8         import org.springframework.stereotype.Component;
  9         import org.springframework.beans.factory.annotation.Value;
 10         import org.springframework.context.support.ClassPathXmlApplicationContext;
 11 
 12         import org.springframework.expression.Expression;
 13         import org.springframework.expression.ExpressionParser;
 14         import org.springframework.expression.spel.standard.SpelExpressionParser;
 15 
 16         public class App {
 17             public static void main(String[] args) {
 18 
 19                 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");
 20                 Shape shape = (Shape) context.getBean("shape");
 21                 shape.display();
 22 
 23                 SpelTest spelTest = (SpelTest) context.getBean("spelTest");
 24                 spelTest.display();
 25 
 26                 ExpressionParser parser = new SpelExpressionParser();
 27                 Expression exp = parser.parseExpression("'Hello SpEL'.bytes.length");
 28                 int length = (Integer) exp.getValue();
 29                 System.out.println(length);
 30 
 31             }
 32         }
 33 
 34         class Number {
 35             private float randomNumber;
 36 
 37             public void setRandomNumber(float randomNumber) {
 38                 this.randomNumber = randomNumber;
 39             }
 40 
 41             public float getRandomNumber() {
 42                 return randomNumber;
 43             }
 44 
 45         }
 46 
 47         class Shape {
 48 
 49             private float num;
 50             private float width;
 51             private String date;
 52             private boolean isEmail;
 53             private float PI;
 54 
 55             public void setNum(float num) {
 56                 this.num = num;
 57             }
 58 
 59             public void setWidth(float width) {
 60                 this.width = width;
 61             }
 62 
 63             public void setDate(String date) {
 64                 this.date = date;
 65             }
 66 
 67             public void setIsEmail(boolean isEmail) {
 68                 this.isEmail = isEmail;
 69             }
 70 
 71             public void setPI(float PI) {
 72                 this.PI = PI;
 73             }
 74 
 75             public void display() {
 76                 System.out.println("Shape -> display(): width = " + width
 77                                                     + ", num = " + num
 78                                                     + ", isEmail = " + isEmail
 79                                                     + ", PI = " + PI
 80                                                     + ", date = " + date);
 81             }
 82         }
 83 
 84         @Component("spel")
 85         class Spel {
 86             @Value("SpEL Name")
 87             private String name;
 88             @Value("#{{\"key\":5,\"value\": \"Test Value\"}}")
 89             public Map<String, String> map;
 90 
 91             public String getName() {
 92                 return name;
 93             }
 94 
 95         }
 96 
 97         @Component("spelTest")
 98         class SpelTest {
 99             @Value("#{spel}")   // 引用 Bean
100             private Spel spel;
101             @Value("#{'local | ' + spel.getName()}")   // 引用 Bean 方法
102             private String name;
103             @Value("#{spel.map['value']}")   // 从集合中取值
104             private String mapValue;
105 
106             //@Value("#{'1,2,3'.split(',')}")
107             @Value("#{{1, 2, 3}}")
108             private int[] array;
109             @Value("#{'\"Java\",\"Spring\",\"Idea\"'.split(',')}")
110             private List<String> list;
111             @Value("#{'\"Tree\",\"Dog\",\"River\"'.split(',')}")
112             private Set<String> set;
113             @Value("#{{\"name\":\"Tester\",\"age\": 21}}")
114             private Map<String, String> map;
115 
116             public void display() {
117                 System.out.println("SpelTest -> display(): name = " + name);
118                 System.out.println("SpelTest -> display(): spel.getName() = " + spel.getName());
119                 System.out.println("SpelTest -> display(): mapValue = " + mapValue);
120                 System.out.println("SpelTest -> display(): array = " +  Arrays.toString(array));
121                 System.out.println("SpelTest -> display(): list = " + list);
122                 System.out.println("SpelTest -> display(): map = " + map);
123                 System.out.println("SpelTest -> display(): set = " + set);
124             }
125         }

    输出:

        Shape -> display(): width = 1000.0, num = 97.401924, isEmail = true, PI = 3.1415927, date = 2020年1月
        SpelTest -> display(): name = local | SpEL Name
        SpelTest -> display(): spel.getName() = SpEL Name
        SpelTest -> display(): mapValue = Test Value
        SpelTest -> display(): array = [1, 2, 3]
        SpelTest -> display(): list = ["Java", "Spring", "Idea"]
        SpelTest -> display(): map = {name=Tester, age=21}
        SpelTest -> display(): set = ["Tree", "Dog", "River"]
        10