• Spring SpEL 表达式语言


     一、文本表达式

    文本表达式支持字符串、 数字(正数 、 实数及十六进制数) 、 布尔类型及 null。其中的字符表达式可使用单引号来表示,形如:'Deniro'。如果表达式中包含单引号或者双引号字符,那么可以使用转义字符 \。

    数字支持负数 、小数、科学记数法、八进制数和十六进制数 。 默认情况下,实数使用 Double.parseDouble() 进行表达式类型转换 。

    1. ExpressionParser parser = new SpelExpressionParser();
    2. //字符串解析, 写成“Hello World”会报错。
    3. String str = (String) parser.parseExpression("'Hello World'").getValue();
    4. System.out.println(str);
    5. //整型解析
    6. int intVal = (Integer) parser.parseExpression("0x2F").getValue();
    7. System.out.println(intVal);
    8. //双精度浮点型解析
    9. double doubleVal = (Double) parser.parseExpression("4329759E+22").getValue();
    10. System.out.println(doubleVal);
    11. //布尔型解析
    12. boolean booleanVal = (boolean) parser.parseExpression("true").getValue();
    13. System.out.println(booleanVal);

    二、对象属性表达式

    在 SpEL 中,我们可以使用对象属性路径(形如类名.属性名.属性名)来访问对象属性的值。

    假设有一个账号类,Account.java:

    1. public class Account {
    2. private String name;
    3. private int footballCount;
    4. private Friend friend;
    5. private List friends;
    6. public Account(String name) {
    7. this.name = name;
    8. }
    9. public void setName(String name) {
    10. this.name = name;
    11. }
    12. public String getName() {
    13. return name;
    14. }
    15. public void setFootballCount(int footballCount) {
    16. this.footballCount = footballCount;
    17. }
    18. public int getFootballCount() {
    19. return footballCount;
    20. }
    21. public void setFriends(List friends) {
    22. this.friends = friends;
    23. }
    24. public List getFriends() {
    25. return friends;
    26. }
    27. public void setFriend(Friend friend) {
    28. this.friend = friend;
    29. }
    30. public Friend getFriend() {
    31. return friend;
    32. }
    33. }

    它包含姓名 name、足球数 footballCount 和一个朋友 friend 属性。friend 属性是一个 Friend 类:

    1. public class Friend {
    2. private String name;
    3. public Friend(String name) {
    4. this.name = name;
    5. }
    6. public String getName() {
    7. return name;
    8. }
    9. }

    解析属性对象表达式

    1. //初始化对象
    2. Account account=new Account("Deniro");
    3. account.setFootballCount(10);
    4. account.addFriend(new Friend("Jack"));
    5. //解析器
    6. ExpressionParser parser
    7. = new SpelExpressionParser();
    8. //解析上下文
    9. EvaluationContext context=new StandardEvaluationContext(account);
    10. //获取不同类型的属性
    11. String name= (String) parser.parseExpression("Name").getValue(context);
    12. System.out.println(name);
    13. int count= (Integer) parser.parseExpression("footballCount+1").getValue(context);
    14. System.out.println(count);
    15. //获取嵌套类中的属性
    16. String friend= (String) parser.parseExpression("friend.name").getValue(context);
    17. System.out.println(friend);

    总结:

    • SpEL 解析器适应力强,属性名首字母大小写均可。
    • 解析对象表达式时,需要传入 EvaluationContext 上下文参数。

    三、数组、List 和 Map 表达式

    数组表达式支持 Java 创建数组的语法,形如 new int[]{3,4,5},数组项之间以逗号作为分隔符。

    注意:目前还不支持多维数组。Map 表达式以键值对的方式来定义,形如 {name:'deniro',footballCount:10}。

    1. //解析器
    2. ExpressionParser parser = new SpelExpressionParser();
    3. //解析一维数组
    4. int[] oneArray = (int[]) parser.parseExpression("new int[]{3,4,5}").getValue();
    5. System.out.println("一维数组开始:");
    6. for (int i : oneArray) {
    7. System.out.println(i);
    8. }
    9. System.out.println("一维数组结束");
    10. //这里会抛出 SpelParseException
    11. //int[][] twoArray = (int[][]) parser.parseExpression("new int[][]{3,4,5}{3,4,5}")
    12. // .getValue();
    13. //解析 list
    14. List list = (List) parser.parseExpression("{3,4,5}").getValue();
    15. System.out.println("list:" + list);
    16. //解析 Map
    17. Map map = (Map) parser.parseExpression("{account:'deniro',footballCount:10}")
    18. .getValue();
    19. System.out.println("map:" + map);
    20. //解析对象中的 list
    21. final Account account = new Account("Deniro");
    22. Friend friend1 = new Friend("Jack");
    23. Friend friend2 = new Friend("Rose");
    24. List friends = new ArrayList<>();
    25. friends.add(friend1);
    26. friends.add(friend2);
    27. account.setFriends(friends);
    28. EvaluationContext context = new StandardEvaluationContext(account);
    29. String friendName = (String) parser.parseExpression("friends[0].name")
    30. .getValue(context);
    31. System.out.println("friendName:" + friendName);

    从数组与 List 获取值,可以在括号内指定索引来获取,形如上例中的 friends[0]。Map 中可通过键名来获取,形如 xxx['xxx']

    四、方法表达式

    SpEL 支持调用有访问权限的方法,这些方法包括对象方法、静态方法,而且支持可变方法参数。除此之外,还可以调用 String 类型中的所有可访问方法,比如 String.contains('xxx')

    1. //解析器
    2. ExpressionParser parser = new SpelExpressionParser();
    3. //调用 String 方法
    4. boolean isEmpty = parser.parseExpression("'Hi,everybody'.contains('Hi')").getValue(Boolean.class);
    5. System.out.println("isEmpty:" + isEmpty);
    6. /**
    7. * 调用对象相关方法
    8. */
    9. final Account account = new Account("Deniro");
    10. EvaluationContext context = new StandardEvaluationContext(account);
    11. //调用公开方法
    12. parser.parseExpression("setFootballCount(11)").getValue(context, Boolean.class);
    13. System.out.println("getFootballCount:" + account.getFootballCount());
    14. //调用私有方法,抛出 SpelEvaluationException: EL1004E: Method call: Method write() cannot be found on net.deniro.spring4.spel.Account type
    15. //parser.parseExpression("write()").getValue(context,Boolean.class);
    16. //调用静态方法
    17. parser.parseExpression("read()").getValue(context, Boolean.class);
    18. //调用待可变参数的方法
    19. parser.parseExpression("addFriendNames('Jack','Rose')").getValue(context, Boolean.class);

    注意:调用对象的私有方法会抛出异常。

    五、操作符表达式

    关系操作符

    SpEL 支持 Java 标准操作符:等于、不等于、小于、小等于、大于、大等于、正则表达式和 instanceof 操作符。

    1. //解析器
    2. ExpressionParser parser = new SpelExpressionParser();
    3. //数值比较
    4. boolean result = parser.parseExpression("2>1").getValue(Boolean.class);
    5. System.out.println("2>1:" + result);
    6. //字符串比较
    7. result = parser.parseExpression("'z'>'a'").getValue(Boolean.class);
    8. System.out.println("'z'>'a':" + result);
    9. //instanceof 运算符
    10. result = parser.parseExpression("'str' instanceof T(String)").getValue(Boolean.class);
    11. System.out.println("'str' 是否为字符串 :" + result);
    12. result = parser.parseExpression("1 instanceof T(Integer)").getValue(Boolean.class);
    13. System.out.println("1 是否为整型 :" + result);
    14. //正则表达式
    15. result = parser.parseExpression("22 matches '\\d{2}'").getValue(Boolean.class);
    16. System.out.println("22 是否为两位数字 :" + result);

    instanceof 操作符后面是类型表达式,格式为 T(Java 包装器类型),如整型 T(Integer)。注意:不能使用原生类型,如果这样 T(int) 会返回错误的判断结果。

    matches 用于定义正则表达式,之后跟着单引号包裹着的正则表达式。

    逻辑操作符

    逻辑操作符支持以下操作:

    逻辑操作符说明
    and 或 &&与操作
    or 或 ||或操作
    !非操作

    注意: 在 SpEL 中,不仅支持 Java 标准的逻辑操作符,还支持 and 与 or 关键字。

    1. //解析器
    2. ExpressionParser parser = new SpelExpressionParser();
    3. //与操作
    4. boolean result = parser.parseExpression("true && true").getValue(Boolean.class);
    5. System.out.println("与操作:" + result);
    6. //或操作
    7. result = parser.parseExpression("true || false").getValue(Boolean.class);
    8. System.out.println("或操作:" + result);
    9. parser.parseExpression("true or false").getValue(Boolean.class);
    10. System.out.println("或操作(or 关键字):" + result);
    11. //非操作
    12. result = parser.parseExpression("!false").getValue(Boolean.class);
    13. System.out.println("非操作:" + result);
    14. //抛出 SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from java.lang.Integer to java.lang.Boolean
    15. //parser.parseExpression("!0").getValue(Boolean.class);

    注意:逻辑操作符前后运算结果必须是布尔类型,否则会抛出 SpelEvaluationException。

    运算操作符

    SpEL 支持 Java 运算操作符,并遵守运算符优先级规则:

    运算操作符说明支持的操作数类型
    +加法数字、字符串或日期
    -减法数字或日期
    *乘法数字
    /除法数字
    %取模数字
    ^指数幂数字
    1. //解析器
    2. ExpressionParser parser = new SpelExpressionParser();
    3. //加法运算
    4. Integer iResult = parser.parseExpression("2+3").getValue(Integer.class);
    5. System.out.println("加法运算:" + iResult);
    6. String sResult = parser.parseExpression("'Hi,'+'everybody'").getValue(String.class);
    7. System.out.println("字符串拼接运算:" + sResult);
    8. //减法运算
    9. iResult = parser.parseExpression("2-3").getValue(Integer.class);
    10. System.out.println("减法运算:" + iResult);
    11. //乘法运算
    12. iResult = parser.parseExpression("2*3").getValue(Integer.class);
    13. System.out.println("乘法运算:" + iResult);
    14. //除法运算
    15. iResult = parser.parseExpression("4/2").getValue(Integer.class);
    16. System.out.println("除法运算:" + iResult);
    17. Double dResult = parser.parseExpression("4/2.5").getValue(Double.class);
    18. System.out.println("除法运算:" + dResult);
    19. //求余运算
    20. iResult = parser.parseExpression("5%2").getValue(Integer.class);
    21. System.out.println("求余运算:" + iResult);

    六、安全导航操作符

    安全导航操作符来源于 Groovy 语言,使用它能够避免空指针异常。一般在访问对象时,需要验证该对象是否为空,使用安全导航操作符就能避免繁琐的空对象验证方法。它的格式是在获取对象属性操作符“.” 之前加一个 “?”。

    1. final Account account = new Account("Deniro");
    2. account.setFriend(new Friend("Jack"));
    3. //解析器
    4. ExpressionParser parser = new SpelExpressionParser();
    5. EvaluationContext context = new StandardEvaluationContext(account);
    6. String friendName = parser.parseExpression("friend?.name").getValue(context, String.class);
    7. System.out.println("friendName:" + friendName);
    8. //设置为 null
    9. account.setFriend(null);
    10. friendName = parser.parseExpression("friend?.name").getValue(context, String.class);
    11. //打印出 null
    12. System.out.println("friendName:" + friendName);

    这里会先判断 friend 对象是否为空;如果为空,则返回 "null" 字符串;否则返回需要的属性值。

    七、三元运算符

    SpEL 支持标准的 Java 三元操作符:<表达式 1>?<表达式 2>:<表达式 3>

    1. ExpressionParser parser = new SpelExpressionParser();
    2. boolean result = parser.parseExpression("(1+2) == 3?true:false").getValue(Boolean.class);
    3. System.out.println("result:" + result);

    八、Elvis 操作符

    Elvis 操作符是在 Groovy 中使用的三元操作符简化版。

    在三元操作符中,我们一般需要写两次变量名,比如下面代码段中的 title:

    1. String title="News";
    2. String actualTitle=(title!=null)?title:"tip";

    使用 Elvis 操作符后,可以将上述代码段简写为:

    title?:"tip"

    SpEL 支持的 Elvis 操作符格式是:?:,如果 var 变量为 null,那就取 value 值,否则就取自身的值。所以 Elvis 操作符很适合用于设置默认值。

    1. final Account account = new Account("Deniro");
    2. //解析器
    3. ExpressionParser parser = new SpelExpressionParser();
    4. EvaluationContext context = new StandardEvaluationContext(account);
    5. String friendName = parser.parseExpression("name?:'无名'").getValue(context, String.class);
    6. System.out.println("friendName:" + friendName);
    7. //设置名字为 null
    8. account.setName(null);
    9. friendName = parser.parseExpression("name?:'无名'").getValue(context, String.class);
    10. System.out.println("friendName:" + friendName);

    九、赋值表达式

    可以通过赋值表达式来设置属性的值,效果等同于调用 setValue() 方法。

    1. final Account account = new Account("Deniro");
    2. //解析器
    3. ExpressionParser parser = new SpelExpressionParser();
    4. EvaluationContext context = new StandardEvaluationContext(account);
    5. String name=parser.parseExpression("name='Jack'").getValue(context,String.class);
    6. System.out.println("name:"+name);

    十、类型操作符

    类型操作符 T 可以从类路径加载指定类名称(全限定名)所对应的 Class 的实例,格式为:T(全限定类名),效果等同于 ClassLoader#loadClass()

    1. ExpressionParser parser = new SpelExpressionParser();
    2. //加载 java.lang.Integer
    3. Class integerClass = parser.parseExpression("T(Integer)").getValue(Class.class);
    4. System.out.println(integerClass == java.lang.Integer.class);
    5. //加载 net.deniro.spring4.spel.Account
    6. Class accountClass = parser.parseExpression("T(com.codedot.spel.Account)").getValue(Class.class);
    7. System.out.println(accountClass == Account.class);
    8. //调用类静态方法
    9. double result = (double) parser.parseExpression("T(Math).abs(-2.5)").getValue();
    10. System.out.println("result:" + result);

    我们还可以直接通过 T 操作符调用类的静态方法,格式为 T(全限定类名).静态方法名,比如上面例子中求某数的绝对值 T(Math).abs(-2.5)

    SpEL 中会使用 StandardTypeLocator#findType() 方法来加载类。 findType 方法定义如下:

    1. public Class findType(String typeName) throws EvaluationException {
    2. String nameToLookup = typeName;
    3. try {
    4. return ClassUtils.forName(nameToLookup, this.classLoader);
    5. }
    6. catch (ClassNotFoundException ey) {
    7. // try any registered prefixes before giving up
    8. }
    9. for (String prefix : this.knownPackagePrefixes) {
    10. try {
    11. nameToLookup = prefix + '.' + typeName;
    12. return ClassUtils.forName(nameToLookup, this.classLoader);
    13. }
    14. catch (ClassNotFoundException ex) {
    15. // might be a different prefix
    16. }
    17. }
    18. throw new SpelEvaluationException(SpelMessage.TYPE_NOT_FOUND, typeName);
    19. }
    • 尝试直接加载类。
    • 如果找不到,则尝试从已注册的包前缀(java.lang)下加载类。所以如果需要加载的类在 java.lang 下,那么可以直接写类名。
    • 如果都找不到,则抛出 SpelEvaluationException 异常。

    十一、创建对象操作符

    可以使用 new 操作符来创建一个新对象 。 除了基本类型(如整型、布尔型等)和字符串之外,创建其它类需要指明全限定类名( 包括包路径 ) 。

    1. ExpressionParser parser = new SpelExpressionParser();
    2. Account account = parser.parseExpression("new com.codedot.spel.Account" + "('Deniro')").getValue(Account.class);
    3. System.out.println("name:"+account.getName());

    十二、变量表达式

    可以通过 #变量名 来引用在 EvaluationContext 中定义的变量。通过 EvaluationContext#setVariable(name, val) 即可定义新的变量;name 表示变量名,val 表示变量值。

    如果变量是集合,比如 list,那么可以通过 #scores.[#this] 来引用集合中的元素。

    1. Account account = new Account("Deniro");
    2. ExpressionParser parser = new SpelExpressionParser();
    3. EvaluationContext context = new StandardEvaluationContext(account);
    4. //定义一个新变量,名为 newVal
    5. context.setVariable("newVal", "Jack");
    6. //获取变量 newVal 的值,并赋值给 User 的 name 属性
    7. parser.parseExpression("name=#newVal").getValue(context);
    8. System.out.println("getName:" + account.getName());
    9. //this 操作符表示集合中的某个元素
    10. List scores = new ArrayList<>();
    11. scores.addAll(Arrays.asList(23.1, 82.3, 55.9));
    12. context.setVariable("scores", scores);//在上下文中定义 scores 变量
    13. List scoresGreat80 = (List) parser.parseExpression("#scores.?[#this>80]")
    14. .getValue(context);
    15. System.out.println("scoresGreat80:" + scoresGreat80);

    十三、集合选择表达式

    可以使用选择表达式来过滤集合,从而生成一个新的符合选择条件的集合 。它的语法是 ?[selectionExpression]。选择符合条件的结果集的第一个元素的语法为 ^ [selectionExpression] ,选择最后一个元素的语法为 $[selectionExpression]。选择表达式也可应用于 Map 。

    1. List list = new ArrayList();
    2. list.add(10);
    3. list.add(21);
    4. list.add(8);
    5. list.add(33);
    6. ExpressionParser parser = new SpelExpressionParser();
    7. //过滤 list 集合中的元素
    8. StandardEvaluationContext listContext = new StandardEvaluationContext(list);
    9. List great4List = (List) parser.parseExpression("?[#this>4]").getValue(listContext);
    10. System.out.println("great4List:" + great4List);
    11. //获取匹配元素中的第一个值
    12. Integer first = (Integer) parser.parseExpression("^[#this>2]").getValue(listContext);
    13. System.out.println("first:" + first);
    14. //获取匹配元素中的最后一个值
    15. Integer end = (Integer) parser.parseExpression("$[#this>2]").getValue(listContext);
    16. System.out.println("end:" + end);

    对于 List 和 Set ,是针对集合中的每一个元素进行比较的;而对于 Map,则可以指定是元素的键(key)还是元素的值进行比较的。

    1. ExpressionParser parser = new SpelExpressionParser();
    2. StandardEvaluationContext context = new StandardEvaluationContext();
    3. //过滤 Map
    4. Map rank = new HashMap<>();
    5. rank.put("Deniro", 96.5);
    6. rank.put("Jack", 85.3);
    7. rank.put("Lily", 91.1);
    8. context.setVariable("Rank", rank);
    9. //value 大于 90
    10. Map rankGreat95 = (Map) parser.parseExpression
    11. ("#Rank.?[value>90]").getValue(context);
    12. System.out.println("rankGreat95:" + rankGreat95);
    13. //key 按字母顺序,排在 L 后面
    14. Map afterL = (Map) parser.parseExpression("#Rank.?[key>'L']").getValue(context);
    15. System.out.println("afterL:" + afterL);

    十四、集合元素布尔判断

    通过表达式 ![projectionExpression],我们可以判断集合中每一个元素是否符合表达式规则。

    1. ExpressionParser parser = new SpelExpressionParser();
    2. List list = (List) parser.parseExpression("{3,4,5}").getValue();
    3. System.out.println("list:" + list);
    4. List isgreat4=(List)parser.parseExpression("![#this>3]").getValue(list);
    5. System.out.println("isgreat4:" + isgreat4);

  • 相关阅读:
    notepad 文本筛选并替换
    pytorch 33 使用mmdeploy将mmdetection模型转换为onnx,并实现c++部署(支持动态nms)
    Flask基础:模板渲染+模板过滤使用+控制语句
    Java学习笔记 第五天 数组与排序
    机器学习——第五章 神经网络
    tomcat
    实用TCP协议(2):TCP 参数优化
    useEffect
    风靡全球25年的重磅IP,新作沦为脚本乐园
    spring常见问题汇总
  • 原文地址:https://blog.csdn.net/qq_39035773/article/details/133122626