当前位置: 首页 > news >正文

Java - Spring 表达式语言 (SpEL) 简单入门

Java - Spring 表达式语言 (SpEL) 简单入门

文章目录

  • Java - Spring 表达式语言 (SpEL) 简单入门
    • 引言
    • 一、环境
    • 二、资料
    • 三、引用SpEL依赖
    • 四、SeEL支持的功能
    • 基础1:获取对象值
    • 基础2:获取对象值
    • 基础3:集合对象象的访问
    • 基础4:使用SeEL对属性赋值
    • 完整测试用例
    • 总结

引言

Spring 表达式语言(SpEL是Spring Expression Language的简称)是一种功能强大的表达式语言,支持在运行时查询和操作对象图。功能定义在spring-expression-6.1.3.jar包中。虽然 SpEL 是 Spring 产品组合中表达式评估的基础,但它不直接与 Spring 绑定,可以独立使用。
以下使用单元测试的形式,对SeEL的使用有个简单了解和入门学习。

一、环境

  • Springboot 3.2.2
  • jdk-17.0.9
  • Maven 3.3.9
  • windows 10

二、资料

  • Spring Expression Language (SpEL)

三、引用SpEL依赖

  • Springboot3中已经默认引用了SpEL包,引用结构是
    • spring-boot-starter-web
      • spring-webmvc
        • spring-expression
  • 独立引用可以配置
<dependency><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>6.1.3</version><scope>compile</scope>
</dependency>

四、SeEL支持的功能

  • 表达式语言支持以下功能:
    • 字面表达式
    • 布尔和关系运算符
    • 正则表达式
    • 类表达式
    • 访问属性、数组、列表和映射
    • 方法调用
    • 关系运算符
    • 调用构造函数
    • bean引用
    • 数组构造
    • 内联的list
    • 内联的map
    • 三元运算符
    • 变量
    • 用户自定义函数
    • 集合选择
    • 模板化表达式

基础1:获取对象值

通过SpEL获取user对象中的id属性。

/*
* #this 和 #root 变量的区别
*   #this 变量始终被定义并引用当前评估对象(针对那些非限定引用被解析)。
*   #root 变量始终被定义并引用根上下文对象。
* */
@Test
public void getValueTest() {var user = new User(11,"zhangsan");// 1 定义解析器SpelExpressionParser parser = new SpelExpressionParser();// 2 使用解析器解析表达式Expression exp = parser.parseExpression("#this.id");// 3 获取解析结果Integer id = (Integer) exp.getValue(user);System.out.println(id);
}

基础2:获取对象值

基于StandardEvaluationContext评估上下文对象,获取user对象值。

/*** 测试SpEL表达式的求值功能* 使用StandardEvaluationContext来设置变量的值,并通过SpelExpressionParser解析表达式,最后通过表达式获取变量的值。*/
@Test
public void getValue4EvaluationTest() {var user = new User(11,"zhangsan");//StandardEvaluationContext context = new StandardEvaluationContext();context.setVariable("parm",user);//定义解析器SpelExpressionParser parser = new SpelExpressionParser();//使用解析器解析表达式Expression exp = parser.parseExpression("#parm.id");//获取解析结果Integer id = (Integer) exp.getValue(context);System.out.println(id);
}

基础3:集合对象象的访问

使用索引[x]的形式访问集合中的user对象的id属性。

/*** 测试SpEL表达式中#this和#root变量的区别* 在SpEL表达式中,#this和#root是两个特殊的变量,它们在使用时有着明显的区别。* #this变量始终被定义并引用当前评估对象(针对那些非限定引用被解析)。* #root变量始终被定义并引用根上下文对象。* 本测试用例演示了如何在SpEL表达式中使用#this变量来获取列表中指定索引的元素属性。*/
@Test
public void getValue4ListTest() {/** #this 和 #root 变量的区别*   #this 变量始终被定义并引用当前评估对象(针对那些非限定引用被解析)。*   #root 变量始终被定义并引用根上下文对象。* */var users = getUsers();print();// 1 定义解析器SpelExpressionParser parser = new SpelExpressionParser();// 2 使用解析器解析表达式Expression exp = parser.parseExpression("#this[2].id");// 3 获取解析结果Integer id = (Integer) exp.getValue(users);System.out.println(id);
}

基础4:使用SeEL对属性赋值

使用SimpleEvaluationContext评估,对user的id属性赋值。

    /*** 测试使用SpEL表达式设置对象属性值* <p>本测试用例演示了如何使用SpEL表达式来设置对象的属性值。首先创建了一个User对象,并打印出来。* 然后定义了SpEL表达式解析器,并构建了用于读写数据绑定的评估上下文。接着使用SpEL表达式"id"来设置User对象的id属性值为22。* 最后,使用另一个SpEL表达式"#this.id"来获取User对象的id属性值,并打印出来。同时,再次打印User对象以验证属性值是否被成功设置。* <p>注意事项:* - #this 变量始终被定义并引用当前评估对象(针对那些非限定引用被解析)。* - #root 变量始终被定义并引用根上下文对象,但在本测试用例中未使用。*/@Testpublic void setValueTest() {/** #this 和 #root 变量的区别*   #this 变量始终被定义并引用当前评估对象(针对那些非限定引用被解析)。*   #root 变量始终被定义并引用根上下文对象。* */var user = new User(11,"zhangsan");System.out.println(user);// 1 定义解析器SpelExpressionParser parser = new SpelExpressionParser();// 2 设置值EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();parser.parseExpression("id").setValue(context,user, 22);// 3 使用解析器解析表达式Expression exp = parser.parseExpression("#this.id");// 4 获取解析结果Integer id = (Integer) exp.getValue(user);System.out.println(id);System.out.println(user);}

完整测试用例

import org.junit.jupiter.api.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;public class SpELTest {List<User> users =  new ArrayList<>();//@BeforeEachpublic List<User> getUsers() {System.out.println("~~~~~~~~~~~~~~~~ init");for (int i = 0; i < 10; i++) {User user = new User();user.id = i;user.name = "name" + i;users.add(user);}return users;}//@AfterEachpublic void print() {System.out.println("~~~~~~~~~~~~~~~~ print");users.forEach(user -> System.out.println(user));}/*** 测试SpEL表达式解析功能* 通过SpEL表达式获取对象的属性值*/@Testpublic void getValueTest() {var user = new User(11,"zhangsan");// 1 定义解析器SpelExpressionParser parser = new SpelExpressionParser();// 2 使用解析器解析表达式Expression exp = parser.parseExpression("#this.id");// 3 获取解析结果Integer id = (Integer) exp.getValue(user);System.out.println(id);}/*** 测试SpEL表达式的求值功能* 使用StandardEvaluationContext来设置变量的值,并通过SpelExpressionParser解析表达式,最后通过表达式获取变量的值。*/@Testpublic void getValue4EvaluationTest() {var user = new User(11,"zhangsan");//StandardEvaluationContext context = new StandardEvaluationContext();context.setVariable("parm",user);//定义解析器SpelExpressionParser parser = new SpelExpressionParser();//使用解析器解析表达式Expression exp = parser.parseExpression("#parm.id");//获取解析结果Integer id = (Integer) exp.getValue(context);System.out.println(id);}/*** 测试SpEL表达式中#this和#root变量的区别* 在SpEL表达式中,#this和#root是两个特殊的变量,它们在使用时有着明显的区别。* #this变量始终被定义并引用当前评估对象(针对那些非限定引用被解析)。* #root变量始终被定义并引用根上下文对象。* 本测试用例演示了如何在SpEL表达式中使用#this变量来获取列表中指定索引的元素属性。*/@Testpublic void getValue4ListTest() {/** #this 和 #root 变量的区别*   #this 变量始终被定义并引用当前评估对象(针对那些非限定引用被解析)。*   #root 变量始终被定义并引用根上下文对象。* */var users = getUsers();print();// 1 定义解析器SpelExpressionParser parser = new SpelExpressionParser();// 2 使用解析器解析表达式Expression exp = parser.parseExpression("#this[2].id");// 3 获取解析结果Integer id = (Integer) exp.getValue(users);System.out.println(id);}/*** 测试使用SpEL表达式设置对象属性值* <p>本测试用例演示了如何使用SpEL表达式来设置对象的属性值。首先创建了一个User对象,并打印出来。* 然后定义了SpEL表达式解析器,并构建了用于读写数据绑定的评估上下文。接着使用SpEL表达式"id"来设置User对象的id属性值为22。* 最后,使用另一个SpEL表达式"#this.id"来获取User对象的id属性值,并打印出来。同时,再次打印User对象以验证属性值是否被成功设置。* <p>注意事项:* - #this 变量始终被定义并引用当前评估对象(针对那些非限定引用被解析)。* - #root 变量始终被定义并引用根上下文对象,但在本测试用例中未使用。*/@Testpublic void setValueTest() {/** #this 和 #root 变量的区别*   #this 变量始终被定义并引用当前评估对象(针对那些非限定引用被解析)。*   #root 变量始终被定义并引用根上下文对象。* */var user = new User(11,"zhangsan");System.out.println(user);// 1 定义解析器SpelExpressionParser parser = new SpelExpressionParser();// 2 设置值EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();parser.parseExpression("id").setValue(context,user, 22);// 3 使用解析器解析表达式Expression exp = parser.parseExpression("#this.id");// 4 获取解析结果Integer id = (Integer) exp.getValue(user);System.out.println(id);System.out.println(user);}private class User {public User() {}public User(int id) {this.id = id;}public User(int id, String name){this.id = id;this.name = name;}public int id;public String name;public String age;@Overridepublic String toString() {return MessageFormat.format("id={0}\tname={1}\tage={2}\t@{3}",id,name,age,Integer.toHexString(hashCode()));}}
}

总结

SpEL语法简单,容易上手,在面向切面编程中有很大的发挥空间。
例如:库表中只记录了用户id没有记录用户名,但界面需要展示用户(userName),类似接口很多如果对每个返回值都处理一遍耗时耗力。
此时可以基于AOP的思想,把需要做转换的方法做切面,监控其返回值。通过SpEL动态读取的user对象的id属性,基于id反查用户名后再回写到user对象的userName属性中(表中无userName字段,但实体中需要定义)。
当然用到SpEL是因为每个表对id的定义不同,可以使用“自定义注解+SpEL表达式”方式,把依赖用到的取值字段定义到user对象的userName属性上,如:@MyRel(“parm.id”),表示userName的反查需要用到当前对象的id属性。
更多的使用场景还需要大家的探索发现。


http://www.mrgr.cn/news/51864.html

相关文章:

  • 科研绘图系列:R语言柱状图(histogram)
  • 操作系统实验二:shell的实现
  • 制造企业数字化转型顶层规划案例(55页满分PPT)
  • 92、Python之异常:异常的概念及异常处理机制
  • MyBatis的占位符(day36)
  • 中科星图GVE案例——利用最短距离方法实现土地分类(合肥)
  • 【JavaEE】——三次握手()详细、易理解
  • Spring 声明式事务
  • 基于 MyBatis Plus 分页封装分页方法
  • 第九课:Python学习之函数基础
  • 2024年的5款AI写作工具,你用过几个?
  • 【含文档】基于Springboot+Vue的仓库管理系统设计与实现(含源码+数据库+lw)
  • 高级IO——五种IO模型
  • 5分钟精通Windows环境变量
  • Cesium的一些计算方法浅析(1)
  • 数据库->库的操作
  • The 48 bit pointer
  • 参加CSP-J/S 认证,要学多久C++才能达到获奖水平?
  • AI学习指南深度学习篇- 预训练模型的原理
  • 常用的网络配置命令