当前位置 : 首页 » 文章分类 :  开发  »  Spring-SpEL表达式

Spring-SpEL表达式

Spring Expression Language(SpEL) SpEL表达式

4 Spring Expression Language (SpEL)
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions

Spring Expression Language Guide
https://www.baeldung.com/spring-expression-language

Spring 从 3.x 开始支持 EL 表达式, Spring Expression Language(SpEL) 是类似于 OGNL 和 JSP EL 的表达式语言, 能够在运行时构建复杂表达式, 存取对象属性、调用对象方法等, 而且所有的 SpEL 都支持 XML 和 Annotation 两种方式, 使用的格式均为: #{SpEL expression}, 例如 @Value("#{'user'.toUpperCase()}")


SpEL表达式求值

4.1 Evaluation
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-evaluation

通用SpEL表达式求值流程

1、构造 SpelExpressionParser SpEL 解析器
2、调用解析器的 parseExpression() 方法解析表达式
3、调用表达式的 getValue() 方法获取结果,返回是 Object 类型需要强制类型转换,或者调用 getValue(@Nullable Class<T> desiredResultType) 返回指定类型

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

@Test
public void testSpEL() {
    // 1、构造解析器
    ExpressionParser parser = new SpelExpressionParser();

    // 2、解析表达式
    Expression expression = parser.parseExpression("'this is a str'");

    // 3、获取结果,返回是 Object 类型
    String strValue = (String) expression.getValue();
}

方法调用示例

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue();

访问JavaBean属性示例

ExpressionParser parser = new SpelExpressionParser();

// invokes 'getBytes()'
Expression exp = parser.parseExpression("'Hello World'.bytes");
byte[] bytes = (byte[]) exp.getValue();

SpEL 还支持嵌套属性,通过点表达式来访问,例如 prop1.prop2.prop3

ExpressionParser parser = new SpelExpressionParser();

// invokes 'getBytes().length'
Expression exp = parser.parseExpression("'Hello World'.bytes.length");
int length = (Integer) exp.getValue();

通过构造方法构造String

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()");
String message = exp.getValue(String.class);

基于上下文根对象求值

// Create and set a calendar
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);

// The constructor arguments are name, birthday, and nationality.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");

ExpressionParser parser = new SpelExpressionParser();

Expression exp = parser.parseExpression("name"); // Parse name as an expression
String name = (String) exp.getValue(tesla);
// name == "Nikola Tesla"

exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean result = exp.getValue(tesla, Boolean.class);
// result == true

其中求值调用了 Object getValue(@Nullable Object rootObject) throws EvaluationException; 方法,表达式都是基于 rootObject 的。


EvaluationContext 求值上下文

变量可以通过 StandardEvaluationContext 的 setVariable() 方法设置到上下文中, 然后再 SpEL 表达式中通过 #变量名 使用变量;

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

其中使用了 Object getValue(EvaluationContext context, @Nullable Object rootObject) throws EvaluationException; 求值方法,从上下文中读。


表达式

文本表达式

文本表达式(Literal Expressions) 支持字符串、数字(int, real, hex)、boolean 和 null.
字符串使用单引号分隔。单引号本身在字符串中使用两个单引号字符表示

ExpressionParser parser = new SpelExpressionParser();

// evals to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// evals to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

Object nullValue = parser.parseExpression("null").getValue();

逻辑运算(布尔运算)

逻辑与 and &&
逻辑或 or ||
逻辑非 not !

// 逻辑运算
System.out.println(parser.parseExpression("true and false").getValue(Boolean.class));
String exp = "!({'abc','efg','xyz'}.contains('abc')) && ('red' == 'red' && 83.2 > 80) || ('man' == 'man')";
System.out.println(parser.parseExpression(exp).getValue(Boolean.class)); // false

集合筛选(#this)

变量 #this 表示当前操作的对象,常用于集合的过滤

// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));

// create parser and set variable 'primes' as the array of integers
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariable("primes",primes);

// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
        "#primes.?[#this>10]").getValue(context);

集合包含判断

// 集合是否包含key - 表达式内inline构造集合
System.out.println(
        parser.parseExpression("{'abc','efg','xyz'}.contains('abc')").getValue(Boolean.class)); // true
System.out.println(
        parser.parseExpression("!{'abc','efg','xyz'}.contains('abc')").getValue(Boolean.class)); // false

// 集合是否包含key - 将集合变量放入上下文,再从上下文中获取集合变量
context.setVariable("set", ImmutableSet.of("red", "green", "blue"));
System.out.println(parser.parseExpression("#set.contains('red')").getValue(context, Boolean.class)); // true
System.out.println(parser.parseExpression("!#set.contains('red')").getValue(context, Boolean.class)); // false

集合元素访问

// 集合元素访问
List<String> strList = Lists.newArrayList("aaa", "bbb", "ccc");
Map<String, Object> myMap = ImmutableMap.of(
        "strAttr", "strAttrValue",
        "intAttr", 1222,
        "doubleAttr", 92.3,
        "listAttr", strList,
        "mapAttr", ImmutableMap.of("k1", "v1")
);
context.setVariable("strList", strList);
context.setVariable("myMap", myMap);
System.out.println(parser.parseExpression("#strList[0]").getValue(context)); // aaa
System.out.println(parser.parseExpression("#myMap['intAttr']").getValue(context)); // 1222

SpEL 在 Java 对象上匹配求值

public class SpELUtils {
    private static final ExpressionParser PARSER = new SpelExpressionParser();

    /**
     * SpEL 表达式匹配求值
     * @param object 要匹配的对象
     * @param condition 表达式
     * @return
     */
    public static boolean spelObjectMatch(Object object, String condition) {
        EvaluationContext context = new StandardEvaluationContext();
        context.setVariable("object", object);
        try {
            return Boolean.TRUE.equals(PARSER.parseExpression(condition).getValue(context, Boolean.class));
        } catch (Exception e) {
            System.out.println("SpEL match error " + e.getMessage());
            return false;
        }
    }
}
@Test
public void testObjectMatch() {
    List<Book> books = Lists.newArrayList(
            new Book("语文书", 9.2),
            new Book("数学书", 10D));
    Student student = Student.builder()
            .name("小明")
            .score(93.4)
            .books(books)
            .attributes(ImmutableMap.of(
                    "strAttr", "strAttrValue",
                    "intAttr", 1222,
                    "doubleAttr", 92.3,
                    "listAttr", books,
                    "mapAttr", ImmutableMap.of("k1", "v1")
                    ))
            .build();

    Assertions.assertTrue(SpELUtils.spelObjectMatch(student, "#object.name == '小明'"));
    Assertions.assertTrue(SpELUtils.spelObjectMatch(student, "#object.score >= 90"));
    // 从 book 数组中筛选出 name=语文书 的书比较其 price 值(注意使用 #this.name=='语文书' 做集合筛选后的结果还是割集合,所以后面取元素要加[0])
    Assertions.assertTrue(SpELUtils.spelObjectMatch(student, "#object.books.?[#this.name=='语文书'][0].price == 9.2"));
    // 从 attributes 的 mapAttr 中获取 k1 的值并比较
    Assertions.assertTrue(SpELUtils.spelObjectMatch(student, "#object.attributes['mapAttr']['k1'] == 'v1'"));
}

@Data
@Builder
private static class Student {
    private String name;
    private Double score;
    private List<Book> books;
    private Map<String, Object> attributes;
}

@Data
@Builder
@AllArgsConstructor
private static class Book {
    private String name;
    private Double price;
}

问题

Property or field ‘xx’ cannot be found on null

SpelEvaluationException: EL1007E: Property or field ‘xx’ cannot be found on null

引用前加上 @
@Document(collection = "#{@cname.collectionName}")

SpelEvaluationException: EL1007E: Property or field ‘cname’ cannot be found on null #15707
https://github.com/spring-projects/spring-boot/issues/15707


上一篇 达梦数据库

下一篇 Spring-Cloud-Gateway

阅读
评论
1.7k
阅读预计8分钟
创建日期 2021-10-18
修改日期 2022-12-12
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论