湖北建设网站信息查询中心,如何建设网站哪个济南兴田德润简介,wordpress仿seowhy模板,哪里有学做ppt的网站标题 前言概述功能使用字面值对象属性和方法变量引用#this 和 #root变量获取类的类型调用对象(类)的方法调用类构造器类型转换运算符赋值运算符条件(关系)表达式三元表达式Elvis 操作符逻辑运算instanceof 和 正则表达式的匹配操作符 安全导航操作员数组集合(Array 、List、Map… 标题 前言概述功能使用字面值对象属性和方法变量引用#this 和 #root变量获取类的类型调用对象(类)的方法调用类构造器类型转换运算符赋值运算符条件(关系)表达式三元表达式Elvis 操作符逻辑运算instanceof 和 正则表达式的匹配操作符 安全导航操作员数组集合(Array 、List、Map)构造列表mapStandardEvaluationContext与SimpleEvaluationContext的区别针对集合的应用 集合筛选集合投影表达式模板rootObjectSpelParserConfigurationEvaluationContextBean引用 案例Maven依赖环境算术计算公式计算数组集合类处理数组数组元素修改 List集合对List集合元素进行修改 Map集合map修改将上下文对象转换为Map对象 前言
关于SpEL表达式写法是很灵活的不要拘泥表面但是前提是你需要知道他有哪些功能支持哪些语法然后就可以花里胡哨的表演了。这块建议顺序看越靠前的越要先懂后面要用到前置知识的还有一点就是他虽然可以那么玩但是很多时候我们可以通过程序转换的就不要SpEL表达式来进行实现。就是说不要过度使用SpEL让他做一些简单的事也侧面反应了SpEL的强大只有你想不到的没他干不了的。
概述
SpEL:(Spring Expression Language) 是一种表达式语言是一种强大,简洁的装配Bean的方式。他可以通过运行期间执行的表达式将值装配到我们的属性或构造函数当中,也可以调用JDK中提供的静态常量,获取外部Properties文件中的的配置。能在运行时构建复杂表达式、存取对象图属性、对象方法调用等等并且能与Spring功能完美整合如能用来配置Bean定义。 表达式语言给静态Java语言增加了动态功能。 SpEL是单独模块只依赖于core模块不依赖于其他模块可以单独使用。
功能
SpEL支持各种操作和函数包括算术运算、逻辑运算、条件判断、正则表达式匹配、集合操作等。它还支持访问上下文中的变量和参数以及调用对象的方法。 使用
任何语言都需要有自己的语法SpEL当然也不例外。所以我们应该能够想到给一个字符串最终解析成一个值这中间至少得经历
字符串 语法分析 生成表达式对象 添加执行上下文 执行此表达式对象 返回结果
关于SpEL的几个概念
表达式“干什么”SpEL的核心所以表达式语言都是围绕表达式进行的。解析器“谁来干”用于将字符串表达式解析为表达式对象。上下文“在哪干”表达式对象执行的环境该环境可能定义变量、定义自定义函数、提供类型转换等等。root根对象及活动上下文对象“对谁干”root根对象是默认的活动上下文对象活动上下文对象表示了当前表达式操作的对象。
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;Data
AllArgsConstructor
NoArgsConstructor
public class MyUser {private String name;private int age;
}import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;import java.util.ArrayList;
import java.util.HashMap;public class SpelTest {public static void main(String[] args) {// 1.创建表达式解析器ExpressionParser parser new SpelExpressionParser();// 2.创建变量上下文,设置变量StandardEvaluationContext ctx new StandardEvaluationContext();//把list和map都放进环境变量里面去ctx.setVariable(myPerson, new MyUser(fsx, 30));ctx.setVariable(myList, new ArrayListString() {{add(fsx);add(周杰伦);}});ctx.setVariable(myMap, new HashMapString, Integer(2) {{put(fsx, 18);put(周杰伦, 40);}});// 3.根据表达式计算结果// MyUser {namefsx, age30}System.out.println(parser.parseExpression(#myPerson).getValue(ctx));// fsxSystem.out.println(parser.parseExpression(#myPerson.name).getValue(ctx));// 安全导航运算符 ?安全导航操作符是用来避免NullPointerExceptionSystem.out.println(parser.parseExpression(#myPerson?.name).getValue(ctx));// setVariable方式取值不能像root一样前缀不可省略~~显然找不到这个key就返回null呗~~~System.out.println(parser.parseExpression(#name).getValue(ctx));// [fsx, 周杰伦]System.out.println(parser.parseExpression(#myList).getValue(ctx));// 周杰伦System.out.println(parser.parseExpression(#myList[1]).getValue(ctx));// 请注意对Map取值两者的区别中文作为key必须用包起来 当然[fsx]也是没有问题的// 18System.out.println(parser.parseExpression(#myMap[fsx]).getValue(ctx));// 40System.out.println(parser.parseExpression(#myMap[周杰伦]).getValue(ctx));// 若采用#key引用的变量不存在返回的是null并不会报错哦System.out.println(parser.parseExpression(#map).getValue(ctx));// 黑科技SpEL内直接可以使用new方式创建实例 能创建数组、List、对象//[Ljava.lang.String;5f2108b5System.out.println(parser.parseExpression(new String[]{java,spring}).getValue());//[java, c语言, PHP]System.out.println(parser.parseExpression({java,c语言,PHP}).getValue());// 静态方法方法调用 T(类的全路径限定名).方法名() 不要使用${}包裹。此方法一般用来引用常量或静态方法System.out.println(parser.parseExpression(T(java.lang.Math).random()).getValue(String.class));// 实例方法调用System.out.println(parser.parseExpression(#myPerson.getName()).getValue(ctx));/*** 调用Bean 中的方法* 如果解析上下文已经配置那么bean解析器能够 从表达式使用符号查找bean类。*/// 此处用DefaultListableBeanFactory做测试,系统运行时可传入ApplicationContextDefaultListableBeanFactory beanFactory new DefaultListableBeanFactory();beanFactory.registerSingleton(user, new MyUser(成龙, 30));ctx.setBeanResolver(new BeanFactoryResolver(beanFactory));// 3. spel解析器执行表达式取得结果System.out.println(parser.parseExpression(user.getName()).getValue(ctx, String.class));}}字面值
这块没啥好讲的基础的基础部分必须会 //Hello, World!
String result parser.parseExpression(Hello, World!).getValue(String.class); SpEL支持的字面量包括字符串、数字类型int、long、float、double、布尔类型、null类型。
class aa{public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();String str1 parser.parseExpression(Hello World!).getValue(String.class);//Hello World!//String str12 parser.parseExpression(name).getValue(String.class);//报错哦,必须要引号int int1 parser.parseExpression(1).getValue(Integer.class);//1int result parser.parseExpression(2 3 * 2).getValue(Integer.class);//8long long1 parser.parseExpression(-1L).getValue(long.class);//-1float float1 parser.parseExpression(1.1).getValue(Float.class);//1.1double double1 parser.parseExpression(1.1E2).getValue(double.class);//110.0int hex1 parser.parseExpression(0xa).getValue(Integer.class);//10long hex2 parser.parseExpression(0xaL).getValue(long.class);//10boolean true1 parser.parseExpression(true).getValue(boolean.class);//trueboolean false1 parser.parseExpression(false).getValue(boolean.class);//falseObject null1 parser.parseExpression(null).getValue(Object.class);//null}
}
对象属性和方法
这块挺有意思的务必会本质就是我们既可以通过属性名也可以通过方法名来获取值底层无非就是反射得到的结果
注意属性名的第一个字母不区分大小写。
Data
public class Dog {private String name;private Friend friend;private Integer[] prices;private ListString ls;
}Data
public class Friend {private String name;public Friend(String name) {this.name name;}
}public class TestOne {public static void main(String[] args) {property();}public static void property(){ExpressionParser parser new SpelExpressionParser();Dog dog new Dog();dog.setName(yq);dog.setFriend(new Friend(yl));// 通过属性yqStandardEvaluationContext context new StandardEvaluationContext(dog);String dogName1 parser.parseExpression(name).getValue(context, String.class);String dogName1N parser.parseExpression(Name).getValue(context, String.class);String dogName2 parser.parseExpression(name).getValue(dog, String.class);String dogName2N parser.parseExpression(Name).getValue(dog, String.class);String dogName3 parser.parseExpression(getName()).getValue(dog, String.class);//通过属性yq1900String dogName1900 parser.parseExpression(name 1900).getValue(dog, String.class);//通过方法yq1900String getDogName (String)parser.parseExpression(getName() 1900).getValue(dog);//通过属性ylString friendName (String) parser.parseExpression(friend.name).getValue(dog);//通过属性方法混合ylString getFriendName1 (String) parser.parseExpression(getFriend().getName()).getValue(dog);String getFriendName2 (String) parser.parseExpression(friend.getName()).getValue(dog);String getFriendName3 (String) parser.parseExpression(getFriend().name).getValue(dog);//yl123String getFriendName (String) parser.parseExpression(getFriend().name123).getValue(dog);}
}
如果你要是用#的形式访问需要setVariable context.setVariable(dog, dog);String dogName11 parser.parseExpression(#dog.name).getValue(context, String.class);System.out.println(dogName11:dogName11);//dogName11:yqString dogName22 parser.parseExpression(#dog.getName()).getValue(context, String.class);System.out.println(dogName22:dogName22);//dogName22:yqString dogName11N parser.parseExpression(#dog.Name).getValue(context, String.class);System.out.println(dogName11N:dogName11N);//dogName11N:yq变量引用
这块没啥好说的啊基础setVariable这种记得用#取
public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();context.setVariable(name, Alice);context.setVariable(age, 25);//Alice is 25 years oldString greeting parser.parseExpression(#name is #age years old).getValue(context, String.class);}
}#this 和 #root变量
#this变量引用当前的评估对象(根据该评估对象解析非限定引用)。
#root变量总是被定义并引用根上下文对象。虽然#this可能会随着表达式的组成部分的计算而变化但是#root总是指根。 这块root开始真理解不了,后来大概了解了
public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();// 正常创建StandardEvaluationContext实例StandardEvaluationContext context new StandardEvaluationContext();ListPerson people new ArrayList();people.add(new Person(Alice, 25));people.add(new Person(Bob, 30));people.add(new Person(Charlie, 20));context.setVariable(people, people);// 使用 #this 筛选年龄大于 20 岁的人基于当前正在处理的元素进行判断ListPerson result1 (ListPerson) parser.parseExpression(#people.?[#this.age 20]).getValue(context);System.out.println(使用 #this 筛选后的人员列表: result1);//[Person(nameAlice, age25), Person(nameBob, age30)]// 使用 #root 结合正确的获取变量方式筛选年龄大于 20 岁的人从根上下文对象获取 people 列表进行判断 下面这个无论我怎么改写都报错开始理解不了下面理解了
// ListPerson result2 (ListPerson) parser.parseExpression(#root.#people.?[#root.#people[age] 20]) .getValue(context);ListPerson result2 (ListPerson) parser.parseExpression(#root.#people.?[#this.age 20]).getValue(context);System.out.println(使用 #root 筛选后的人员列表: result2);//使用 #root 筛选后的人员列表: [Person(nameAlice, age25), Person(nameBob, age30)]}}Data
AllArgsConstructor
class Person {private String name;private int age;
}
#root真正玩法 ExpressionParser parser new SpelExpressionParser();// 正常创建StandardEvaluationContext实例ListPerson people new ArrayList();people.add(new Person(Alice, 25));people.add(new Person(Bob, 30));people.add(new Person(Charlie, 20));StandardEvaluationContext context new StandardEvaluationContext(people);Object value parser.parseExpression(#root).getValue(context);//原始人员列表: [Person(nameAlice, age25), Person(nameBob, age30), Person(nameCharlie, age20)]System.out.println(原始人员列表: value);Object value1 parser.parseExpression(#root[0]).getValue(context);//第一个人员: 第一个人员: Person(nameAlice, age25)System.out.println(第一个人员: value1);// 使用 #root 结合正确的获取变量方式筛选年龄大于 20 岁的人从根上下文对象获取 people 列表进行判断ListPerson result2 (ListPerson) parser.parseExpression(#root.?[#this.age 20]).getValue(context);//使用 #root 筛选后的人员列表: [Person(nameAlice, age25), Person(nameBob, age30)]System.out.println(使用 #root 筛选后的人员列表: result2);下面我再次尝试root用法,跟我想象中的写法不一样
public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();ListEmployee employees new ArrayList();employees.add(new Employee(4000.0));employees.add(new Employee(6000.0));employees.add(new Employee(2000.0));Department department new Department(employees);StandardEvaluationContext context new StandardEvaluationContext(department);// 计算部门平均工资 #root.employees 等价 employeesdouble averageSalary ((ListEmployee) parser.parseExpression(employees).getValue(context)).stream().mapToDouble(Employee::getSalary).average().orElse(0.0);System.out.println(部门平均工资: averageSalary);//部门平均工资: 4000.0// 将平均工资作为变量存储在上下文中context.setVariable(averageSalary, averageSalary);Object expression parser.parseExpression(employees).getValue(context);//[Employee(salary4000.0), Employee(salary6000.0), Employee(salary2000.0)]System.out.println(expression);Object value parser.parseExpression(#averageSalary).getValue(context);//#root#averageSalary 等价 #averageSalary 4000.0System.out.println(value);ListEmployee result2 (ListEmployee) parser.parseExpression(employees.?[#this.salary #averageSalary]).getValue(context);System.out.println(工资高于部门平均工资的员工数量: result2.size());//工资高于部门平均工资的员工数量: 1}
}Data
AllArgsConstructor
class Employee {private double salary;
}Data
AllArgsConstructor
class Department {private ListEmployee employees;
}
获取类的类型
这块是必须要知道的 可以使用特殊的T运算符来指定java.lang.Class的实例(类型)。静态方法也是通过使用这个操作符来调用的。
StandardEvaluationContext使用TypeLocator来查找类型StandardTypeLocator(可以替换)是基于对java.lang包的理解而构建的。所以java.lang中类型的T()引用不需要使用全限定名但是其他包中的类必须使用全限定名。 public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();//class java.util.DateClass dateClass parser.parseExpression(T(java.util.Date)).getValue(Class.class);//class java.lang.StringClass stringClass parser.parseExpression(T(String)).getValue(Class.class);//class com.example.AnimalClass stringClass2 parser.parseExpression(T(com.example.Animal)).getValue(Class.class);//CEILING 2 FLOOR3 trueboolean trueValue parser.parseExpression(T(java.math.RoundingMode).CEILING T(java.math.RoundingMode).FLOOR).getValue(Boolean.class);//调用类中静态方法测试String value parser.parseExpression(T(com.example.Animal).getText(调用类中静态方法)).getValue(String.class);}}class Animal {public static String getText(String text) {return text 测试;}
}
调用对象(类)的方法
还可以调用对象的方法有些强
class Calculator {public int add(int a, int b) {return a b;}public static void main(String[] args) {Calculator calculator new Calculator();ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext(calculator);int result parser.parseExpression(add(10, 20)).getValue(context, Integer.class);//30// 调用String类的substring方法 cdeString bc parser.parseExpression(abcdef.substring(2, 5)).getValue(String.class);}
}调用类静态方法
public class Peo {public static void main(String[] args) throws NoSuchMethodException {ExpressionParser parser new SpelExpressionParser();EvaluationContext context SimpleEvaluationContext.forReadOnlyDataBinding().build();// 获取要调用的方法context.setVariable(strToUpperCase,Peo.class.getDeclaredMethod(strToUpperCase, String.class));// HELLOString helloWorldReversed parser.parseExpression(#strToUpperCase(hello)).getValue(context, String.class);}// 准备一个要调用的目标方法public static String strToUpperCase(String input) {return input.toUpperCase();}}调用类构造器
使用new运算符调用构造函数。除了基本类型(int、float等)和String之外所有类型都应该使用完全限定的类名。 public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();// 使用new运算符调用Person类的构造函数创建对象Person person (Person) parser.parseExpression(new com.example.Person(Alice, 25)).getValue(context);//创建的人员姓名: Alice, 年龄: 25System.out.println(创建的人员姓名: person.getName() , 年龄: person.getAge());// 使用new运算符调用ArrayList的构造函数创建空的ArrayList对象//下面这个写法报错 Expression [new java.util.ArrayListString()] 23: EL1050E: The arguments (...) for the constructor call are missing
//ArrayListString list (ArrayListString) parser.parseExpression(new java.util.ArrayListString()).getValue(context);ListString list (ListString) parser.parseExpression(new java.util.ArrayList()).getValue(context, List.class);list.add(元素1);list.add(元素2);System.out.println(创建的ArrayList包含元素: list);//创建的ArrayList包含元素: [元素1, 元素2]// 使用new运算符调用Date类的构造函数创建表示当前时间的Date对象Date date (Date) parser.parseExpression(new java.util.Date()) .getValue(context);System.out.println(创建的Date对象表示的时间: date);//创建的Date对象表示的时间: Fri Dec 27 14:20:21 CST 2024}}
Data
AllArgsConstructor
class Person {private String name;private int age;
}
类型转换
public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();context.setVariable(numberStr,123);int number parser.parseExpression(T(Integer).parseInt(#numberStr)).getValue(context, Integer.class);// 创建一个表示当前时间的Date对象Date currentDate new Date();context.setVariable(currentDate, currentDate);// 定义日期格式化的格式字符串这里设置为yyyy-MM-dd HH:mm:ss表示年-月-日 时:分:秒String formatStr yyyy-MM-dd HH:mm:ss;context.setVariable(formatStr, formatStr);// 使用SpEL表达式将Date对象转换为指定格式的字符串// 使用SpEL表达式将Date对象转换为指定格式的字符串String dateStr parser.parseExpression(new java.text.SimpleDateFormat(#formatStr).format(#currentDate)).getValue(context, String.class);System.out.println(转换后的日期字符串为: dateStr);//转换后的日期字符串为: 2024-12-31 14:45:25}
}
运算符 假设有一个用户类User我们需要根据用户的某些属性动态计算某个值可以使用SpEL表达式如下
// 假设已经获取到ExpressionParser和StandardEvaluationContext实例
ExpressionParser parser new SpelExpressionParser();
StandardEvaluationContext context new StandardEvaluationContext();
User user new User();
user.setName(John);
user.setAge(30);
context.setVariable(user, user);
// 使用SpEL表达式计算年龄是否大于25
String expression #user.age 25;
Boolean result parser.parseExpression(expression).getValue(context, Boolean.class);
System.out.println(result); // 输出 true赋值运算符
若要给对象设置属性请使用赋值运算符()。这通常在对setValue的调用中完成但也可以在对getValue的调用中完成。
public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();//这块 context都对我文章中也提到过两者区别注释掉的功能更丰富强大但性能低根据不同场景切换即可//StandardEvaluationContext context new StandardEvaluationContext();EvaluationContext context SimpleEvaluationContext.forReadWriteDataBinding().build();Inventor inventor new Inventor();parser.parseExpression(Name).setValue(context, inventor, Aleksandar Seovic);System.out.println(inventor.getName()); // Aleksandar Seovic// 或者这样赋值String aleks parser.parseExpression(Name Aleksandar Seovic2).getValue(context, inventor, String.class);System.out.println(inventor.getName()); // Aleksandar Seovic2}
}
Data
class Inventor{private String name;
}
条件(关系)表达式
SpEL还提供 等于、不等于(!)、大于()、大于等于()、小于()、小于等于()区间between运算,还有除(/)取模(%)取反(!) SpEL同样提供了等价的“EQ” 、“NE”、 “GT”、“GE”、 “LT” 、“LE”来表示等于、不等于、大于、大于等于、小于、小于等于DIV(/)、MOD(%) 、NOT(!)不区分大小写。 null不被视为任何东西(即不为零)。因此任何其他值总是大于null (X null总是为真)并且没有任何其他值小于零(X null总是为假)。 在 SpEL 中这种比较被定义为任何非null的值都被认为大于null并且没有值被认为小于null。
import lombok.Data;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;public class Peo {public static void main(String[] args) {Person person new Person();person.setName(John Doe);person.setAge(30);ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext(person);//trueboolean isAdult parser.parseExpression(age 18).getValue(context, Boolean.class);//Hello, adultString greeting parser.parseExpression(age 18 ? Hello, adult : Hello, child).getValue(context, String.class);//正确String istrue parser.parseExpression(5 EQ 5 ? 正确 : 错误).getValue(context, String.class);// true 这块 boolean Boolean 结果都一样boolean between1 parser.parseExpression(1 between {1,2}).getValue(boolean.class);// 比较一个正数和null trueboolean result1 parser.parseExpression(5 null).getValue( Boolean.class);// 比较一个负数和null trueboolean result2 parser.parseExpression(-3 null).getValue(Boolean.class);// 尝试比较一个值小于null根据规则应该总是为假 falseboolean result3 parser.parseExpression(2 null).getValue( Boolean.class);boolean result4 parser.parseExpression(null null).getValue( Boolean.class);//falseboolean result5 parser.parseExpression(null null).getValue( Boolean.class);//falseboolean result7 parser.parseExpression(null ! null).getValue( Boolean.class);//falseboolean result6 parser.parseExpression(null null).getValue( Boolean.class);//true}
}
Data
class Person {private String name;private int age;
} 三元表达式 public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();Student student new Student(20,70.0, 22);context.setVariable(student, student);//成年人String result parser.parseExpression(#student.age 18? 成年人 : 未成年人).getValue(context,String.class);//都满足String result2 parser.parseExpression(#student.score 60 #student.attendance 20?都满足:不满足).getValue(context, String.class);}}
Data
AllArgsConstructor
class Student{private Integer age;private Double score;private Integer attendance;
}
Elvis 操作符
在三元运算符语法中你通常要将一个变量重复两次 - (name ! null ? name : Unknown)改进 elvis - (name?:Unknown)
public class Peo {//Elvis运算符是三元运算符语法的缩写用于Groovy语言中。public static void main(String[] args) throws NoSuchMethodException {ExpressionParser parser new SpelExpressionParser();EvaluationContext context SimpleEvaluationContext.forReadOnlyDataBinding().build();/*** 处理可能为 null 的字符串属性* 假设有一个 Person 类包含 nickname昵称属性有时候这个属性可能没有被赋值即为 null* 我们希望在获取这个属性值用于展示等情况时如果是 null 就使用一个默认值来代替就可以使用 Elvis 操作符。*/// 创建一个Person对象设置昵称Person person1 new Person(小机灵);// 使用Elvis操作符获取昵称如果昵称不为null就返回昵称本身为null则返回暂无昵称String displayNickname1 parser.parseExpression(nickname?:暂无昵称).getValue(context, person1, String.class);System.out.println(人员1的昵称: displayNickname1);//人员1的昵称: 小机灵// 创建另一个Person对象不设置昵称默认为nullPerson person2 new Person(null);// 同样使用Elvis操作符获取昵称String displayNickname2 parser.parseExpression(nickname?:暂无昵称).getValue(context, person2, String.class);System.out.println(人员2的昵称: displayNickname2);//人员2的昵称: 暂无昵称ListStudent students new ArrayList();students.add(new Student(80));students.add(new Student(null));students.add(new Student(90));context.setVariable(students, students);// 使用Elvis操作符将学生成绩列表中为null的成绩替换为0后计算平均成绩// 修正后的表达式完整写出Elvis操作符对应的三元表达式形式Double averageGrade2 ((ListInteger) parser.parseExpression(#students.![grade!null?grade:0]).getValue(context, students, List.class)).stream().mapToDouble(Integer::doubleValue).average().orElse(0.0);System.out.println(学生平均成绩: averageGrade2);}
}Data
AllArgsConstructor
class Person {private String nickname;
}Data
AllArgsConstructor
class Student {private Integer grade;
}
逻辑运算
SpEL支持以下逻辑运算符and、or、not 这块不难理解看看就行 import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();//下面要调用方法所以这块调用了有参构造传入对象StandardEvaluationContext context new StandardEvaluationContext(new Peo());// 结果 falseboolean falseValue parser.parseExpression(true and false).getValue(Boolean.class);// 调用方法并根据方法返回值判断 falseString expression1 isMember(Nikola Tesla) and isMember(Mihajlo Pupin);boolean trueValue1 parser.parseExpression(expression1).getValue(context, Boolean.class);// -- OR -- trueboolean trueValue2 parser.parseExpression(true or false).getValue(Boolean.class);// 调用方法并根据方法返回值判断 trueString expression3 isMember(Nikola Tesla) or isMember(Albert Einstein);boolean trueValue3 parser.parseExpression(expression3).getValue(context, Boolean.class);// -- NOT -- 取反 falseboolean falseValue4 parser.parseExpression(!true).getValue(Boolean.class);// 调用方法并根据方法返回值判断 falseString expression5 !isMember(Nikola Tesla) or isMember(Albert Einstein);boolean trueValue5 parser.parseExpression(expression5).getValue(context, Boolean.class);System.out.println(trueValue5);// -- AND and NOT -- trueString expression6 isMember(Nikola Tesla) and !isMember(Mihajlo Pupin);boolean falseValue6 parser.parseExpression(expression6).getValue(context, Boolean.class);}public static boolean isMember(String name){if(name.equals(Nikola Tesla)){return true;}if(name.equals(Albert Einstein)){return false;}return false;}} instanceof 和 正则表达式的匹配操作符
使用基本类型时要小心因为它们会立即被装箱为包装器类型所以1 instanceof T(int)会计算为false而1 instanceof T(Integer)会计算为true。秉承一切皆对象平时推荐大家使用基本数据类型的包装类避免不必要的踩坑
instanceof 这块注释掉的写法是报错的我不理解为什么会报错所以就把所有类setVariable换了种写法 根据它的报错信息 EL1007E: Property or field com cannot be found on null它压根就没解析为一个路径经过后来研究类全限定名你需要用T()包起来是我蠢了与君共勉
package com.example;import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();Cat cat new Cat();context.setVariable(pet, cat);context.setVariable(cat, com.example.Cat.class);context.setVariable(dog, com.example.Dog.class);context.setVariable(animal, com.example.Animal.class);boolean falseValue1 parser.parseExpression(xyz instanceof T(Integer)).getValue(Boolean.class);boolean falseValue2 parser.parseExpression(1 instanceof T(Integer)).getValue(Boolean.class);boolean trueValue1 parser.parseExpression(1 instanceof T(String)).getValue(Boolean.class);boolean trueValue2 parser.parseExpression(1 instanceof T(Integer)).getValue(Boolean.class);// 检查pet是否是Cat类型的实例 true
// boolean result1 parser.parseExpression(#pet instanceof com.example.Cat).getValue(context, Boolean.class);boolean result1 parser.parseExpression(#pet instanceof #cat).getValue(context, Boolean.class);boolean result11 parser.parseExpression(#pet instanceof T(com.example.Cat)).getValue(context, Boolean.class);System.out.println(pet是否是Cat类型的实例: result1);// 检查pet是否是Animal类型的实例 false
// boolean result2 parser.parseExpression(#pet instanceof com.example.Animal).getValue(context, Boolean.class);boolean result2 parser.parseExpression(#pet instanceof #dog).getValue(context, Boolean.class);System.out.println(pet是否是Animal类型的实例: result2);// 检查pet是否是Dog类型的实例 true
// boolean result3 parser.parseExpression(#pet instanceof com.example.Dog).getValue(context, Boolean.class);boolean result3 parser.parseExpression(#pet instanceof #animal).getValue(context, Boolean.class);System.out.println(pet是否是Dog类型的实例: result3);}}class Animal {}
class Cat extends Animal {}
class Dog extends Animal {}
正则表达式 SpEL 也支持使用正则表达式其中对应的关键字为 match。 假设我们要检查一个字符串是否是有效的电子邮件地址格式这只是一个简单示例实际的电子邮件验证更复杂。 这块没啥好说的注意写法就OK了
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;public class Peo {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();String email testexample.com;context.setVariable(emailAddress, email);
// context.setVariable(emailRegex, ^[a-zA-Z0-9][.-][a-zA-Z0-9][a-zA-Z0-9.-]\\.[a-zA-Z0-9.-]$);context.setVariable(emailRegex, ^[a-zA-Z][a-zA-Z0-9.-]\\.[a-zA-Z]{2,}$);// 使用正则表达式检查是否是有效的电子邮件格式boolean result1 parser.parseExpression(#emailAddress matches [a-zA-Z0-9_.-][a-zA-Z0-9-]\\.[a-zA-Z0-9-.]).getValue(context, Boolean.class);boolean result2 parser.parseExpression(#emailAddress matches #emailRegex).getValue(context, Boolean.class);boolean result3 parser.parseExpression(9184qq.com matches #emailRegex).getValue(context, Boolean.class);}}
安全导航操作员
安全导航操作符用于避免NullPointerException来自Groovy语言。通常当引用一个对象时可能需要在访问该对象的方法或属性之前验证它不为null。为了避免这种情况安全导航运算符返回null而不是引发异常。
在 Spring 表达式语言SpEL中安全导航运算符Safe Navigation Operator用于避免空指针异常的情况它的语法形式是在属性访问时使用 ?. 来替代常规的 .当对象为 null 时整个表达式求值不会抛出空指针异常而是返回 null。 Data
AllArgsConstructor
class Address {private String city;
}Data
AllArgsConstructor
class Person {private Address address;}class SpELSafeNavigationOperatorExample1 {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();SimpleEvaluationContext context SimpleEvaluationContext.forReadOnlyDataBinding().build();// 创建一个有地址的Person对象Address address new Address(Beijing);Person person1 new Person(address);// 使用安全导航运算符获取城市名称String cityName1 parser.parseExpression(address?.city).getValue(context, person1, String.class);System.out.println(人员1所在城市: cityName1);//人员1所在城市: Beijing// 创建一个没有地址地址为null的Person对象Person person2 new Person(null);// 使用安全导航运算符获取城市名称此时不会抛出空指针异常而是返回nullString cityName2 parser.parseExpression(address?.city).getValue(context, person2, String.class);System.out.println(人员2所在城市: cityName2);//人员2所在城市: null}
}
在集合元素的属性访问中使用 下面这块我不理解明明注释掉的写法挺正确但是就报错我服了望指正
Data
AllArgsConstructor
class Book {private String title;
}
Data
AllArgsConstructor
class Student {private ListBook books;
}class SpELSafeNavigationOperatorExample2 {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();SimpleEvaluationContext context SimpleEvaluationContext.forReadOnlyDataBinding().build();// 创建一个有书籍的Student对象Book book1 new Book(Math Book);ListBook books new ArrayList();books.add(book1);Student student1 new Student(books);// 使用安全导航运算符获取第一本书的书名// 下面这个写法报错 不理解 Expression [books?.[0].title] 5: EL1049E: Unexpected data after .: lsquare([)
// String bookTitle12 parser.parseExpression(books?.[0].title)
// .getValue(context, student1, String.class);// 使用正确语法的嵌套表达式先判断books是否为null再获取第一本书的书名String bookTitle1 parser.parseExpression((books!null?books[0].title:null)).getValue(context, student1, String.class);System.out.println(学生1的第一本书名: bookTitle1);// 创建一个没有书籍书籍列表为null的Student对象Student student2 new Student(null);// 使用安全导航运算符获取第一本书的书名不会抛出空指针异常返回null
// String bookTitle2 parser.parseExpression(books?.[0].title)String bookTitle2 parser.parseExpression((books!null?books[0].title:null)).getValue(context, student2, String.class);System.out.println(学生2的第一本书名: bookTitle2);// 创建一个有书籍但第一本书为null的Student对象Student student3 new Student(new ArrayList());student3.getBooks().add(null);// 使用安全导航运算符获取第一本书的书名同样不会抛出空指针异常返回null 下面写法又报错服了
// String bookTitle3 parser.parseExpression(books?.[0].title)String bookTitle3 parser.parseExpression((books!null?books[0]!null?books[0].title:null:null)).getValue(context, student3, String.class);System.out.println(学生3的第一本书名: bookTitle3);}
}
数组集合(Array 、List、Map)
这块必须掌握这里描述的简单这块建议多看我下面案例模块的那块代码案例多加深理解 数组和 list 都是通过 [下标获取] map 的内容是通过在括号内指定字面的 key 值来获得的 public class CollectTest {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();EvaluationContext context SimpleEvaluationContext.forReadOnlyDataBinding().build();Dog dog new Dog();dog.setPrices(new Integer[]{1,2,3});dog.setLs(Arrays.asList(pz, yq, yl, xx));MapString, String build new HashMap();build.put(f, pz);build.put(s, yq);build.put(t, yl);dog.setLsMap( build );//2Integer invention1 parser.parseExpression(prices[1]).getValue(context, dog, Integer.class);Integer invention2 parser.parseExpression(getPrices[1]).getValue(context, dog, Integer.class);//yqString invention3 parser.parseExpression(ls[1]).getValue(context, dog, String.class);String invention4 parser.parseExpression(getLs[1]).getValue(context, dog, String.class);//yqString value1 parser.parseExpression(lsMap[s]).getValue(dog, String.class);String value2 parser.parseExpression(getLsMap[s]).getValue(dog, String.class);}
} /*** #numbers引用上下文中的 numbers 集合变量。* .?表示这是一个集合选择操作。* [#this 8]定义了筛选条件#this 关键字引用当前遍历到的集合元素表达式 #this 8 用于检查每个元素是否大于 8。*/ListInteger numbers Arrays.asList(5, 10, 3, 8, 12);context.setVariable(numbers, numbers);// 使用选择表达式筛选出大于8的元素ListInteger filteredList (ListInteger) parser.parseExpression(#numbers.?[#this 8]).getValue(context, List.class);System.out.println(大于8的元素: filteredList);//大于8的元素: [10, 12]构造列表map
在SpEL中可以使用{e1,e2,e3}的形式来构造一个List 如果我们希望构造的List的元素还是一个List则可以将构造的List的元素定义为{e1,e2,e3}这样的形式如{ {1,2},{3,4,5},{6,7,8,9} }。如果需要构造一个空的List则直接将对应的表达式字符串定义为{}即可。
我们知道Map是可以key-value的形式存在的在SpEL中如果我们需要构造一个Map则可以使用{key1:value1,key2:value2}这样的形式进行定义即使用大括号包起来然后key和value之间以冒号:分隔构成一个Entry多个Entry之间以逗号分隔。如果需要构造一个空的Map则只需指定对应的表达式为{:}即可。
对于数组的构造就比较简单了我们可以在表达式中使用Java代码中new的语法来构造一个数组。如new int[]{1,2,3} 如果需要构造一个空数组则可以直接new一个空的数组。多维数组也是支持的但是多维数组只支持定义一个空的数组对于需要初始化指定数组元素的定义暂时在SpEl中是不支持的。
这块就是快速构建一个集合一般来说测试时候用。正常情况下我们是已经有集合了再说也没必须要这样构建集合了解即可 public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();//内联列表[1, 2, 3, 4]ListInteger value (List)parser.parseExpression({1,2,3,4}).getValue();System.out.println(value);//内联Map {nameNikola, dob10-July-1856}Map inventorInfo (Map) parser.parseExpression({name:Nikola,dob:10-July-1856}).getValue();System.out.println(inventorInfo);// {name{firstNikola, lastTesla}, dob{day10, monthJuly, year1856}}Map mapOfMaps (Map) parser.parseExpression({name:{first:Nikola,last:Tesla},dob:{day:10,month:July,year:1856}}).getValue();System.out.println(mapOfMaps);//Array 构造 [0, 0, 0, 0]int[] numbers1 (int[]) parser.parseExpression(new int[4]).getValue();System.out.println(numbers1);System.out.println(Arrays.toString(numbers1));// Array with initializer [1, 2, 3]int[] numbers2 (int[]) parser.parseExpression(new int[]{1,2,3}).getValue();System.out.println(Arrays.toString(numbers2));// Multi dimensional array二维数组不能初始化int[][] numbers3 (int[][]) parser.parseExpression(new int[4][5]).getValue();System.out.println(Arrays.toString(numbers3[0]));//[0, 0, 0, 0, 0]System.out.println(Arrays.toString(numbers3[1]));//[0, 0, 0, 0, 0]System.out.println(Arrays.toString(numbers3[3]));//[0, 0, 0, 0, 0]} StandardEvaluationContext与SimpleEvaluationContext的区别
StandardEvaluationContext 完整的上下文功能 SimpleEvaluationContext 精简版的上下文去除了Java类型参照、构造器、Bean参照等功能 功能丰富度 StandardEvaluationContext 这是一个功能强大且完整的求值上下文。它支持 SpEL 语言的全部特性包括属性访问、方法调用、构造函数调用、变量绑定、类型转换等多种复杂操作。可以在其中设置变量、注册自定义函数等多种自定义操作为表达式求值提供了丰富的上下文环境。 SimpleEvaluationContext 它是一种简化的求值上下文。主要用于只读数据绑定场景对 SpEL 的功能进行了限制。它不支持一些复杂的操作如方法调用除了在特定的白名单中的方法、构造函数调用、变量绑定等。这样的限制使得它在安全性和性能方面有一定的优势特别是在只需要进行简单的属性访问的场景下。 性能和安全性 StandardEvaluationContext 由于其功能全面支持的操作繁多在内部实现上相对复杂。这可能会导致在一些简单场景下性能不如SimpleEvaluationContext。同时因为它支持更多可能修改系统状态的操作如方法调用、变量绑定等如果使用不当可能会带来安全风险例如恶意构造的表达式可能会调用不期望的方法或者修改系统状态。 SimpleEvaluationContext 通过限制功能其内部实现相对简单在性能上有一定优势特别是在只需要进行简单属性访问的大规模数据处理场景下。并且由于限制了可能有风险的操作在安全性方面更有保障确保表达式求值不会产生意外的副作用。 // 明确初始化成绩数组确保有值double[] scoresArray {80.0, 90.0, 75.0};Student student new Student(Alice, scoresArray, null, null);ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext(student);// EvaluationContext context SimpleEvaluationContext.forReadOnlyDataBinding().build();/*** 在你提供的代码中虽然使用StandardEvaluationContext和SimpleEvaluationContext都得到了相同的结果* 但这是因为表达式scoresArray[1]只是简单地访问了数组中的一个元素属性访问。* 如果表达式变得更复杂比如需要调用Student类的方法或者绑定变量那么SimpleEvaluationContext可能就无法满足需求会抛出异常。* 例如如果表达式是#student.getName().toUpperCase()假设Student类有getName方法* 使用SimpleEvaluationContext就会因为不支持方法调用而无法正确求值而StandardEvaluationContext可以正确处理这种情况。*/Double getTwo parser.parseExpression(scoresArray[1]).getValue(context, student, Double.class);System.out.println(getTwo);针对集合的应用
class testA{public static void main(String[] args) {getListValue();}/*** 获取满足条件的集合的值* .?[ ] 结构就是在 SpEL 中实现基于特定条件对集合进行筛选* 以获取符合期望条件的元素子集的一种语法手段。*/private static void getListValue() {Account account new Account(Deniro);ExpressionParser parser new SpelExpressionParser();EvaluationContext context new StandardEvaluationContext(account);//将数据转换成集合ListDouble scores new ArrayList();scores.addAll(Arrays.asList(23.1, 82.3, 55.9));context.setVariable(scores, scores);//在上下文中定义 scores 变量ListDouble scoresGreat80 (ListDouble) parser.parseExpression(#scores.?[#this80]).getValue(context);//[82.3]System.out.println(scoresGreat80);}DataAllArgsConstructorNoArgsConstructorpublic static class Account {private String name;}
}集合筛选
在 Spring 表达式语言SpEL中.?[selectionExpression]是一种用于集合筛选的语法结构。它应用于集合类型如List、Set等可以从集合中筛选出满足selectionExpression条件的元素并返回一个新的集合这个新集合包含了原始集合中符合条件的元素。
Data
AllArgsConstructor
class Person {private String name;private int age;
}class SpELCollectionFilterExample {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();ListPerson people new ArrayList();people.add(new Person(Alice, 25));people.add(new Person(Bob, 18));people.add(new Person(Charlie, 22));context.setVariable(people, people);// 使用.?[selectionExpression]语法筛选年龄大于20岁的人ListPerson filteredPeople (ListPerson) parser.parseExpression(#people.?[age 20]).getValue(context);System.out.println(年龄大于20岁的人员列表:filteredPeople);//[Person(nameAlice, age25), Person(nameCharlie, age22)]}
}
在上述代码中#people.?[age 20]这个表达式就是使用了集合筛选语法。#people是在上下文中定义的人员列表变量.?[age 20]部分是筛选条件age 20就是selectionExpression它表示从#people列表中筛选出年龄大于 20 岁的Person对象。最后将筛选后的结果存储在filteredPeople列表中并打印输出。
selectionExpression的细节
引用当前元素在selectionExpression中可以通过#this来引用当前正在被遍历和判断的集合元素。例如如果要筛选出名字长度大于 3 的人员表达式可以写成#people.?[#this.name.length() 3]。这里#this就代表了people列表中的每一个Person对象通过#this.name.length()获取每个人员姓名的长度来进行条件判断。复杂条件组合selectionExpression可以包含复杂的逻辑表达式。比如要筛选出年龄大于 20 岁并且名字以A开头的人员可以使用表达式#people.?[age 20 #this.name.startsWith(‘A’)]。在这里使用了逻辑与操作符来组合两个条件只有同时满足年龄大于 20 岁和名字以A开头这两个条件的人员才会被筛选出来。访问嵌套属性和方法如果集合元素是复杂对象其中包含其他对象或者有多层嵌套的属性和方法也可以在selectionExpression中进行访问。例如假设Person类中有一个Address类型的属性Address类中有一个city属性要筛选出居住在某个城市比如New York的人员可以这样写表达式#people.?[address.city ‘New York’]前提是Person类中有getAddress方法来获取Address对象。这展示了可以在筛选表达式中深入访问集合元素的嵌套属性来构建复杂的筛选条件。
集合投影
基本概念 在 Spring 表达式语言SpEL中集合投影Collection Projection是一种操作它允许你从集合中的每个元素提取特定的属性或执行一个操作并将结果收集到一个新的集合中。这种操作可以方便地对集合中的数据进行转换和提取。
语法形式 集合投影的语法一般是 collection.![projectionExpression]。其中collection 是要进行投影操作的集合对象例如一个 List、Set 等![projectionExpression] 是投影表达式用于指定从集合元素中提取或计算的内容。
Data
AllArgsConstructor
class Person {private String name;private int age;
}class SpELCollectionProjectionExample {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();ListPerson people new ArrayList();people.add(new Person(Alice, 25));people.add(new Person(Bob, 18));people.add(new Person(Charlie, 22));context.setVariable(people, people);// 使用集合投影语法提取人员姓名列表ListString names (ListString) parser.parseExpression(#people.![name]).getValue(context);System.out.println(人员姓名列表: names);// [Alice, Bob, Charlie]}
}表达式模板
基本概念 Spring 表达式语言SpEL中的表达式模板提供了一种在文本块中嵌入 SpEL 表达式的方式。它允许你将静态文本和动态的 SpEL 表达式组合在一起就像在一个模板中填充动态内容一样。这种方式在生成动态消息、构建 SQL 查询语句、配置文件中的动态属性设置等场景非常有用。
语法形式 表达式模板使用#{}来包裹 SpEL 表达式。在解析时SpEL 会先计算#{}中的表达式然后将结果替换到模板中相应的位置最终得到一个完整的字符串或者其他类型的值取决于模板的使用场景。
注意事项 转义字符在表达式模板中如果需要在#{}外面使用#{或}这些字符可能需要进行转义以避免语法错误。具体的转义规则可能因使用场景和解析器的配置而有所不同。 表达式求值顺序在复杂的表达式模板中要注意 SpEL 表达式的求值顺序可能会影响最终结果。如果有多个#{}表达式它们会按照在模板中的出现顺序依次求值并替换。 上下文变量的可见性确保在表达式模板中使用的上下文变量通过context.setVariable等方式设置的变量在解析表达式时是可见的并且变量的类型和属性符合表达式中的引用要求否则可能会出现求值错误。
Data
AllArgsConstructor
class User {private String username;private String email;public String getUsername() {return username;}public String getEmail() {return email;}
}class SpELExpressionTemplateExample {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();User user new User(JohnDoe, johndoeexample.com);
// StandardEvaluationContext context new StandardEvaluationContext(user);StandardEvaluationContext context new StandardEvaluationContext();context.setVariable(user, user); // 设置变量到上下文中// 使用SpEL表达式 TODO 这个写法报错理解不了为何报错 // Expression [亲爱的 #{user.username} 欢迎您注册我们的平台 您的注册邮箱是 #{user.email} 请妥善保管 ] 4: EL1041E: After parsing a valid expression, there is still more data in the expression: hash(#)String welcomeEmailContent parser.parseExpression(亲爱的 #{user.username} 欢迎您注册我们的平台 您的注册邮箱是 #{user.email} 请妥善保管 ).getValue(context, String.class);String welcomeEmailContent2 亲爱的 parser.parseExpression(#user.username).getValue(context, String.class) 欢迎您注册我们的平台 您的注册邮箱是 parser.parseExpression(#user.email).getValue(context, String.class) 请妥善保管;// 使用纯SpEL表达式String welcomeEmailContent3 parser.parseExpression(T(java.lang.String).format(亲爱的 %s 欢迎您注册我们的平台 您的注册邮箱是 %s 请妥善保管, #user.username, #user.email)).getValue(context, String.class);System.out.println(welcomeEmailContent);System.out.println(welcomeEmailContent2);System.out.println(welcomeEmailContent3);}
}
经过我的尝试你要想通过#{}来实现占位符替换 模板写法为#{[待替换字符]} 我们还要引入模板TemplateParserContext,以及最后的数据值封装为Map public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();User user new User(JohnDoe, johndoeexample.com);String message\亲爱的 #{[userName]}欢迎您注册我们的平台您的注册邮箱是 #{[email]}请妥善保管。\;TemplateParserContext templateParserContextnew TemplateParserContext();Expression expression parser.parseExpression(message,templateParserContext);//模拟数据MapString,Object mapnew HashMap();map.put(userName,user.getUsername());map.put(email,johndoeexample.com);String value expression.getValue(map, String.class);System.out.println(value);//亲爱的 JohnDoe欢迎您注册我们的平台您的注册邮箱是 johndoeexample.com请妥善保管。}实现二表达式模板 TemplateParserContext public static void main(String[] args) {//创建解析器SpelExpressionParser parser new SpelExpressionParser();//创建解析器上下文ParserContext context new TemplateParserContext(%{, });Expression expression parser.parseExpression(你好:%{#name},我们正在学习:%{#lesson}, context);//创建表达式计算上下文EvaluationContext evaluationContext new StandardEvaluationContext();evaluationContext.setVariable(name, 路人甲java);evaluationContext.setVariable(lesson, spring高手系列!);//获取值String value expression.getValue(evaluationContext, String.class);System.out.println(value);//你好:路人甲java,我们正在学习:spring高手系列!}其他玩法 public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();String expressionStr Random number : #{T(java.lang.Math).random() * 100};String result parser.parseExpression(expressionStr, new TemplateParserContext()).getValue(String.class);System.out.println(result);//Random number : 随机数字expressionStr the year is #{T(java.util.Calendar).getInstance().get(T(java.util.Calendar).YEAR)};Expression expression parser.parseExpression(expressionStr, new TemplateParserContext());System.out.println(expression.getValue());//the year is 2024expressionStr 这是一个公司信息详情信息页,公司名称:#{[companyName]},公司法人:#{[legalPerson]}公司电话:#{[companyTel]},公司地址:#{[companyAddress]};TemplateParserContext templateParserContext new TemplateParserContext();expression parser.parseExpression(expressionStr, templateParserContext);//模拟数据MapString, String map new HashMap();map.put(companyName, 特斯拉上海有限公司);map.put(legalPerson, 马斯克);map.put(companyTel, 123456);map.put(companyAddress, 中国上海自由贸易试验区临港新片区江山路5000号);String value expression.getValue(map, String.class);//这是一个公司信息详情信息页,公司名称:特斯拉上海有限公司,公司法人:马斯克公司电话:123456,公司地址:中国上海自由贸易试验区临港新片区江山路5000号System.out.println(value);}rootObject
rootObject是在StandardEvaluationContext标准评估上下文中设置的一个对象它是 SpEL 表达式求值的根对象。当 SpEL 表达式中没有明确指定从哪个对象获取属性或调用方法时默认就会从rootObject开始查找和操作。
Data
AllArgsConstructor
public class Person {private String name;private int age;public String introduce() {return 我叫 name 今年 age 岁。;}
}class SpelRootObjectExample {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();Person person new Person(张三, 25);/*** 当通过parser.parseExpression(name).getValue(context, String.class)获取name属性值时* 由于没有指定从哪个对象获取SpEL 就会从rootObject即person对象中查找name属性并返回其值。* 同理parser.parseExpression(introduce()).getValue(context, String.class)* 会调用rootObjectperson对象的introduce方法并返回结果。*/// 设置rootObjectStandardEvaluationContext context new StandardEvaluationContext(person);// 直接通过属性名获取rootObject的属性值String name parser.parseExpression(name).getValue(context, String.class);System.out.println(姓名 name);//姓名张三// 调用rootObject的方法String introduction parser.parseExpression(introduce()).getValue(context, String.class);System.out.println(introduction);//我叫张三今年25岁。}
}
SpelParserConfiguration
SpelParserConfiguration在 Spring Expression LanguageSpEL中是一个用于配置 SpEL 解析器行为的类 配置解析器的行为细节例如是否允许数组或集合元素的自动增长、是否启用编译器等。这些配置可以影响 SpEL 表达式的解析和求值过程以满足不同的应用场景和需求。
自动增长数组和集合 默认行为在默认情况下当尝试向一个已满的数组或集合中添加元素时SpEL 会抛出异常。 配置自动增长可以通过SpelParserConfiguration来配置允许数组或集合自动增长。
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;import java.util.ArrayList;
import java.util.List;public class SpelParserConfigurationExample {public static void main(String[] args) {// 配置允许集合自动增长SpelParserConfiguration config new SpelParserConfiguration(true, true);SpelExpressionParser parser new SpelExpressionParser(config);ListString list new ArrayList();list.add(item1);list.add(item2);StandardEvaluationContext context new StandardEvaluationContext();context.setVariable(myList, list);// 向已满的集合中添加元素由于配置了自动增长不会抛出异常parser.parseExpression(#myList.add(item3)).getValue(context);System.out.println(context.getVariable(myList));}
}
在上述示例中通过SpelParserConfiguration(true, true)创建了一个配置对象其中两个true参数分别表示允许数组自动增长和允许集合自动增长。然后将此配置传递给SpelExpressionParser这样在后续的表达式求值中当向已满的集合添加元素时集合会自动增长而不会报错。 EvaluationContext
EvaluationContext是 SpEL 表达式求值时的上下文它包含了表达式中可能引用到的变量、对象、函数、类型等信息为表达式的求值提供了一个运行时环境。
提供变量和对象访问可以将需要在表达式中使用的变量或对象设置到EvaluationContext中表达式在求值时就能从上下文中获取这些变量或对象的值或引用进行相应的操作如获取对象的属性、调用对象的方法等。 支持类型引用和函数注册可以在上下文中注册自定义类型和函数使得表达式能够使用这些自定义的类型和函数进行求值。例如可以注册一个自定义的工具函数然后在表达式中调用该函数进行特定的计算或处理。
StandardEvaluationContext这是 SpEL 中最常用的EvaluationContext实现类。
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;public class SpelEvaluationContextExample {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();// 设置变量context.setVariable(myVar, Hello, SpEL!);// 解析表达式并求值String result parser.parseExpression(#myVar).getValue(context, String.class);System.out.println(result); }
}
使用注意事项 对象引用和生命周期管理当将对象设置到EvaluationContext中时需要注意对象的引用和生命周期。如果对象在上下文中被引用但在外部被修改或销毁可能会导致表达式求值出现意外结果。 上下文的线程安全性在多线程环境下使用EvaluationContext时需要确保其线程安全性。如果多个线程同时修改或访问同一个EvaluationContext可能会导致数据不一致或其他并发问题。可以根据具体情况考虑使用线程安全的EvaluationContext实现类或采取适当的同步措施。 Bean引用
如果已经用bean解析器配置了评估上下文则可以使用符号从表达式中查找bean。 要访问工厂bean本身应该在bean名称前加上符号 这块博主还没深入研究乏了等后续用到再看吧
引入相关依赖以 Maven 项目为例假设使用 Spring Boot 方便搭建环境你也可以根据实际情况调整为普通 Spring 项目依赖配置 在 pom.xml 文件中添加以下依赖
dependencies!-- Spring Boot 核心依赖包含了 Spring 相关基础功能 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactIdversion2.7.14/version/dependency!-- Spring 表达式语言依赖 --dependencygroupIdorg.springframework/groupIdartifactIdspring-expression/artifactIdversion5.3.23/version/dependency
/dependencies创建普通 Bean 和工厂 Bean 示例类
import org.springframework.stereotype.Component;Component
public class UserService {private String serviceName User Service;public String getServiceName() {return serviceName;}public void setServiceName(String serviceName) {this.serviceName serviceName;}public void printUserInfo() {System.out.println(执行用户服务相关操作服务名称: serviceName);}
}import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component;Component
public class UserServiceFactoryBean implements FactoryBeanUserService {Overridepublic UserService getObject() throws Exception {UserService userService new UserService();userService.setServiceName(Custom User Service from Factory);return userService;}Overridepublic Class? getObjectType() {return UserService.class;}Overridepublic boolean isSingleton() {return true;}
}在上述代码中 UserService 是一个普通的 Spring Bean提供了一些简单的业务方法这里只是简单打印服务名称用于示例。 UserServiceFactoryBean 是一个实现了 FactoryBean 接口的工厂 Bean它负责创建 UserService 的实例并且可以在创建过程中对实例进行一些自定义的初始化操作比如修改服务名称等。
自定义 MyBeanResolver用于演示自定义解析 Bean 的逻辑
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import java.util.HashMap;
import java.util.Map;public class MyBeanResolver {private MapString, Object beanMap new HashMap();public MyBeanResolver() {// 模拟创建并放入一些Bean实例到自定义的解析器中UserService userService new UserService();beanMap.put(userService, userService);try {UserServiceFactoryBean factoryBean new UserServiceFactoryBean();beanMap.put(userServiceFactoryBean, factoryBean);beanMap.put(userServiceFactoryBean, factoryBean); // 同时放入工厂 Bean 本身引用} catch (Exception e) {e.printStackTrace();}}public Object resolve(EvaluationContext context, String beanName) throws AccessException {return beanMap.get(beanName);}
}这里自定义的 MyBeanResolver 通过一个 Map 来模拟存储了 UserService 这个普通 Bean 和 UserServiceFactoryBean 这个工厂 Bean 及其本身的引用以 开头的键值对形式存储在 resolve 方法中根据传入的 Bean 名称从 Map 里获取对应的 Bean 实例返回。
使用 MyBeanResolver 进行 Bean 引用示例包含普通 Bean 和工厂 Bean 引用
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;public class SpELBeanReferenceExampleWithMyResolver {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();// 设置自定义的Bean解析器context.setBeanResolver(new MyBeanResolver());// 通过 符号引用普通 Bean 并调用其方法Object userServiceBean parser.parseExpression(userService).getValue(context);if (userServiceBean instanceof UserService) {((UserService) userServiceBean).printUserInfo();}// 通过 符号引用工厂 Bean 本身Object factoryBean parser.parseExpression(userServiceFactoryBean).getValue(context);if (factoryBean instanceof UserServiceFactoryBean) {try {UserService userServiceFromFactory ((UserServiceFactoryBean) factoryBean).getObject();userServiceFromFactory.printUserInfo();} catch (Exception e) {e.printStackTrace();}}}
}在这个示例中 首先创建了 ExpressionParser 和 StandardEvaluationContext然后设置了自定义的 MyBeanResolver。 通过 parser.parseExpression(“userService”).getValue(context); 使用 符号引用了名为 userService 的普通 Bean并调用其 printUserInfo 方法展示可以正常操作该普通 Bean。 通过 parser.parseExpression(“userServiceFactoryBean”).getValue(context); 使用 符号引用了名为 userServiceFactoryBean 的工厂 Bean 本身获取到工厂 Bean 实例后调用其 getObject 方法获取由工厂创建的 UserService 实例再调用该实例的 printUserInfo 方法展示了如何访问工厂 Bean 本身以及通过它获取创建的 Bean 实例进行操作。
使用 BeanFactoryResolver 进行 Bean 引用示例同样包含普通 Bean 和工厂 Bean 引用基于 Spring 应用上下文 以下是基于 Spring 应用上下文使用 BeanFactoryResolver 的示例这里通过创建一个简单的 Spring 配置类来模拟获取应用上下文实际应用中通常是通过启动 Spring 容器等方式获取完整的应用上下文
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.BeanFactoryResolver;
import org.springframework.expression.spel.support.StandardEvaluationContext;Configuration
public class AppConfig {Beanpublic UserService userService() {return new UserService();}Beanpublic UserServiceFactoryBean userServiceFactoryBean() {return new UserServiceFactoryBean();}
}public class SpELBeanReferenceExampleWithBeanFactoryResolver {public static void main(String[] args) {// 创建Spring应用上下文AnnotationConfigApplicationContext applicationContext new AnnotationConfigApplicationContext(AppConfig.class);ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext();// 设置BeanFactoryResolver传入应用上下文context.setBeanResolver(new BeanFactoryResolver(applicationContext));// 通过 符号引用普通 Bean 并调用其方法Object userServiceBean parser.parseExpression(userService).getValue(context);if (userServiceBean instanceof UserService) {//执行用户服务相关操作服务名称: User Service((UserService) userServiceBean).printUserInfo();}// 通过 符号引用工厂 Bean 本身Object factoryBean parser.parseExpression(userServiceFactoryBean).getValue(context);if (factoryBean instanceof UserServiceFactoryBean) {try {UserService userServiceFromFactory ((UserServiceFactoryBean) factoryBean).getObject();userServiceFromFactory.printUserInfo();//执行用户服务相关操作服务名称: Custom User Service from Factory} catch (Exception e) {e.printStackTrace();}}// 关闭应用上下文释放资源等applicationContext.close();}
}在这个示例里 先是通过 Configuration 注解定义了 AppConfig 这个 Spring 配置类在里面使用 Bean 注解定义了 UserService 这个普通 Bean 和 UserServiceFactoryBean 这个工厂 Bean让 Spring 容器可以管理它们的创建和生命周期。 然后在 main 方法中创建了 AnnotationConfigApplicationContext 作为 Spring 的应用上下文基于配置类来初始化相关的 Bean 实例。 接着创建 ExpressionParser 和 StandardEvaluationContext并设置 BeanFactoryResolver 传入应用上下文使得后续 SpEL 表达式能基于 Spring 的 Bean 管理机制进行操作。 之后同样通过 符号引用普通 Bean 和通过 符号引用工厂 Bean 本身并进行相应的操作展示最后关闭应用上下文释放资源。 通过以上示例你可以清晰地看到在不同的 Bean 解析器配置下自定义的 MyBeanResolver 和系统自带的 BeanFactoryResolver 如何在 SpEL 中通过 符号引用普通 Bean 以及通过 符号引用工厂 Bean 本身来进行相应的操作这在实际基于 Spring 的项目开发中对于灵活地在表达式中操作各种 Bean 实例很有帮助。你可以根据实际业务场景进一步扩展和调整这些示例代码以满足具体的需求。
案例
你要是案例中的语法看不懂上面使用篇幅都有对应的内容讲解
以下是我在jdk17下进行的测试代码案例
在 spring-context 包中已经引入 spring-expression 包 在其他非Spring的项目中可以单独引入
dependencygroupIdorg.springframework/groupIdartifactIdspring-expression/artifactIdversion5.0.0.RELEASE/versionscopecompile/scope
/dependencyMaven依赖环境
我这块是一个SpringBoot项目我把所有依赖都拿过来了你按需引入即可 dependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web-services/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdcom.squareup.okhttp3/groupIdartifactIdokhttp/artifactIdversion4.10.0/version/dependencydependencygroupIdcom.google.code.gson/groupIdartifactIdgson/artifactIdversion2.9.1/version/dependencydependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.75/version/dependencydependencygroupIdcom.belerweb/groupIdartifactIdpinyin4j/artifactIdversion2.5.0/version/dependency!-- dependencygroupIdcom.fasterxml.jackson.core/groupIdartifactIdjackson-databind/artifactIdversion2.13.1/version/dependency--!-- PostgreSQL --dependencygroupIdorg.postgresql/groupIdartifactIdpostgresql/artifactIdscoperuntime/scope/dependencydependencygroupIdcom.alibaba/groupIdartifactIddruid/artifactIdversion1.1.13/version/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jdbc/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-jpa/artifactId/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-spring-boot3-starter/artifactIdversion3.5.7/version/dependencydependencygroupIdorg.apache.commons/groupIdartifactIdcommons-lang3/artifactIdversion3.13.0/version/dependencydependencygroupIdcom.google.guava/groupIdartifactIdguava/artifactIdversion32.1.2-jre/version/dependency/dependencies算术计算
算术运算SpEL 支持的算术运算可以是加、减、乘、除、求余、指数等 这块就是你已经得到了具体数值想进行算术运算得到运行结果 2行代码就能搞定 public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();//当表达式需要引用外部变量、对象属性或者执行方法调用等更复杂的操作时就需要使用StandardEvaluationContext。这块不涉及可省略//StandardEvaluationContext context new StandardEvaluationContext();//double result parser.parseExpression((80.0 90.0 75.0) / 3).getValue(context, Double.class);//直接解析并计算表达式 也可以BigDecimal类型哦double result parser.parseExpression((80.0 90.0 75.0)/3).getValue(Double.class);// 使用DecimalFormat进行格式化保留两位小数DecimalFormat decimalFormat new DecimalFormat(#.##);String formattedResult decimalFormat.format(result);System.out.println(计算结果为: formattedResult);//计算结果为: 81.67/*** 升级理解 StandardEvaluationContext的用途案例* 通过context.setVariable(x, 20.0)将变量x的值设置为20.0然后在表达式#x 10中通过#x引用这个变量最后计算得到结果30.0。* 所以StandardEvaluationContext提供了一种在表达式中使用外部变量和对象的机制使SpEL的功能更加强大。*/StandardEvaluationContext context new StandardEvaluationContext();context.setVariable(x, 20.0);double result2 parser.parseExpression(#x 10).getValue(context, Double.class);System.out.println(计算结果为: result2);//计算结果为: 30.0}
public class New {public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();System.out.println((parser.parseExpression((12)*5 8-6/2)).getValue());//加减乘除 20System.out.println((parser.parseExpression(8%3).getValue()));//求余 2System.out.println((parser.parseExpression(2.0e3).getValue()));//指数 2000.0System.out.println((parser.parseExpression(2^3).getValue()));//指数 8}
}公式计算
在实际业务当中假如我们商品价格不是固定不变的我们通过中台系统配置会配置对应商品的一个价格计算公式后续希望根据定义好的公式去计算对应商品价格。
import lombok.Data;Data
public class Product {private String name;private double price;private double discountRate;private String formula;
}import org.springframework.stereotype.Service;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;Service
public class PricingService {public double calculateFinalPrice(Product product) {ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext(product);// 计算最终价格原价 * (1 - 折扣率)// return parser.parseExpression(price * (1 - discountRate)).getValue(context, Double.class);return parser.parseExpression(product.getFormula()).getValue(context, Double.class);}public static void main(String[] args) {Product product new Product();product.setPrice(50);product.setDiscountRate(0.5);product.setFormula(price * (1 - discountRate));PricingService pricingService new PricingService();System.out.println(pricingService.calculateFinalPrice(product));//25.0}
}
数组集合类处理
假设我们正在开发一个学生管理系统需要处理学生的成绩信息。成绩可能以不同的集合形式存储并且需要根据一定的规则进行查询和计算。我们将使用 SpEL 来操作数组、列表List和映射Map这些集合类型。
Data
AllArgsConstructor
public class Student {private String name;private double[] scoresArray;private ListDouble scoresList;private MapString, Double subjectScoresMap;
}数组
注意啊 这块什么时候要用#整明白了 在Spring的SpEL中使用 # 前缀来引用上下文中定义的变量。当您在 StandardEvaluationContext 中通过 setVariable方法设置一个变量时您可以在SpEL表达式中使用 # 前缀来访问这个变量。
Student 类中scoresArray 是一个属性而不是上下文中的变量。 在SpEL表达式中直接使用 变量名来访问这个属性而不是使用 # 前缀。这是因为 scoresArray 是 student 对象的一个字段而不是在 EvaluationContext 中定义的变量。 如果您想要在SpEL表达式中使用 # 前缀来访问 scoresArray您需要在 StandardEvaluationContext 中将其设置为一个变量这就是为什么您需要调用 context.setVariable(“scoresArray”, student.getScoresArray());。这样scoresArray 就成为了上下文中的一个变量而不是 student 对象的一个属性。
下面是实现一个计算数组平均值的简单案例其本质就是方括号符号[]获得的 /*** SpEL 操作数组Array* 计算学生成绩数组的平均分* 通过#scoresArray[索引]的形式访问数组元素然后计算数组中成绩的平均值。*/public static void main(String[] args) {// 明确初始化成绩数组确保有值double[] scoresArray {80.0, 90.0, 75.0};Student student new Student(Alice, scoresArray, null, null);ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext(student);//这块我们关于数组的任意操作基本都有对应语法实现简单写几个Double getOne parser.parseExpression(scoresArray[0]).getValue(context, student, Double.class);System.out.println(getOne);//80.0Double getThree parser.parseExpression(scoresArray.length).getValue(context, student, Double.class);System.out.println(getThree);//3.0//这块我在提一嘴如果你想判断数组中是否包含某个值可以使用Arrays.asList转换为List后调用contains方法。这块目的是让你知道对应的写法// 将double[]转换为Double[]Double[] boxedArray Arrays.stream(scoresArray).boxed().toArray(Double[]::new);student.setBoxedArray(boxedArray);//必须设置这个新属性不然它是获取不到这个新数组的// 利用Arrays.asList转换为List后调用contains方法boolean containsValue parser.parseExpression(T(java.util.Arrays).asList(boxedArray).contains(75.0)).getValue(context, student, Boolean.class);System.out.println(containsValue);// 修改表达式通过循环计算数组元素总和再除以数组长度来求平均值String expressionStr 0;for (int i 0; i scoresArray.length; i) {expressionStr ( expressionStr scoresArray[ i ]);}expressionStr / scoresArray.length;double averageScoreArray parser.parseExpression(expressionStr).getValue(context, Double.class);/*double averageScoreArray parser.parseExpression((((0 scoresArray[0]) scoresArray[1]) scoresArray[2]) / scoresArray.length).getValue(context, Double.class);*/System.out.println(Average score of array: averageScoreArray);//Average score of array: 81.66666666666667//现在 你要想#的形式获取就必须设置 setVariable 但是我明明都包装到student里面了呀 暂时没理解 context.setVariable(scoresArray, student.getScoresArray());Double getTwo parser.parseExpression(#scoresArray[1]).getValue(context, student, Double.class);System.out.println(getTwo);//90.0double averageScoreArray2 parser.parseExpression((((0 #scoresArray[0]) #scoresArray[1]) #scoresArray[2]) / #scoresArray.length).getValue(context, Double.class);System.out.println(Average score of array: averageScoreArray2);//Average score of array: 81.66666666666667}
数组元素修改
本质就是 parser.parseExpression(#myArray[1] 5).getValue(context) public static void main(String[] args) {// 创建数组int[] numbers {1, 2, 3};// 创建表达式解析器ExpressionParser parser new SpelExpressionParser();// 创建评估上下文并将包含数组的对象设置进去这里直接把数组本身设置进去也可以是包含数组的自定义对象等情况StandardEvaluationContext context new StandardEvaluationContext();context.setVariable(myArray, numbers);// 后续进行数组元素修改相关的表达式操作// 解析表达式并修改数组中索引为1的元素值将原本的2修改为5parser.parseExpression(#myArray[1] 5).getValue(context);// 验证修改结果输出修改后的数组元素 修改后的数组[1, 5, 3]System.out.println(修改后的数组Arrays.toString(numbers));}List集合
这块模拟下List直接取值跟List集合中在嵌套一个集合的取值其本质就是方括号符号[]获得的 import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;public class SpELListExample {/*** 集合及嵌套集合的简单操作*/public static void main(String[] args) {// 定义Parser可以定义全局的parserExpressionParser parser new SpelExpressionParser();// 创建一个只读数据绑定的评估上下文如果需要读写数据绑定可以用其他方式创建EvaluationContext context SimpleEvaluationContext.forReadOnlyDataBinding().build();// 初始化Tesla对象及相关数据Tesla tesla new Tesla();tesla.setInventions(Arrays.asList(交流电, 特斯拉线圈, 无线通信, X光机));// 初始化IEEE对象及相关数据IEEE ieee new IEEE();Member member new Member();member.setName(John Doe);member.setInventions(Arrays.asList(发明1, 发明2, 发明3, 发明4, 发明5, 发明6, 发明7, 发明8));ListMember members new ArrayListMember();members.add(member);ieee.setMembers(members);// 将Tesla对象放入评估上下文context.setVariable(tesla, tesla);// 将IEEE对象放入评估上下文context.setVariable(ieee, ieee);// 取出tesla对象的inventions 第四个数据String invention parser.parseExpression(inventions[3]).getValue(context, tesla, String.class);//Tesla的第四个发明是: X光机System.out.println(Tesla的第四个发明是: invention);// 取出ieee对象的第一个Member的name属性 这块属性的首字母大小写是不影响的String name parser.parseExpression(Members[0].Name).getValue(context, ieee, String.class);//IEEE第一个成员的名字是: John DoeSystem.out.println(IEEE第一个成员的名字是: name);Object name2 parser.parseExpression(Members[0]).getValue(context, ieee);//Members[0]是: Member(nameJohn Doe, inventions[发明1, 发明2, 发明3, 发明4, 发明5, 发明6, 发明7, 发明8])System.out.println(Members[0]是: name2.toString());// 取出ieee对象的第一个Member中的第七个InventionsString invention2 parser.parseExpression(Members[0].Inventions[6]).getValue(context, ieee, String.class);// IEEE第一个成员的第七个发明是: 发明7System.out.println(IEEE第一个成员的第七个发明是: invention2);}
}// 代表特斯拉相关信息的类
Data
ToString
class Tesla {private ListString inventions;
}// 代表IEEE组织成员相关信息的类
Data
ToString
class Member {private String name;private ListString inventions;
}// 代表IEEE组织相关信息的类
Data
ToString
class IEEE {private ListMember members;
}class Other{/*** SpEL 操作列表List* 找出学生成绩列表中的最高分* 这里使用T()操作符来引用java.util.Collections类然后调用max方法找出成绩列表中的最高分*/public static void main(String[] args) {ListDouble scoresList Arrays.asList(85.0, 92.0, 78.0);Student student new Student(Bob, null, scoresList, null);ExpressionParser parser new SpelExpressionParser();StandardEvaluationContext context new StandardEvaluationContext(student);double maxScoreList parser.parseExpression(T(java.util.Collections).max(scoresList)).getValue(context, Double.class);System.out.println(Max score of list: maxScoreList);//Max score of list: 92.0}
}
对List集合元素进行修改
本质就是 parser.parseExpression(format, parserContext).setValue(context, tarVal) import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.CollectionUtils;import java.util.List;
import java.util.Map;Slf4j
public class ListSpELTest {DataAllArgsConstructorNoArgsConstructorpublic static class Account {private String name;}DataAllArgsConstructorpublic static class Info {private String url ;}DataAllArgsConstructorpublic static class User {private Long id;private String name;private List infos;}public static void main(String[] args) {spEL2FillList();spEL2Map();}/*** 这块比如我们有一个用户它有多个收获地址url现在我们对它的收获地址进行批量修改*/private static void spEL2FillList() {ExpressionParser parser new SpelExpressionParser();EvaluationContext context new StandardEvaluationContext();//解析模板#{}TemplateParserContext parserContext new TemplateParserContext();User user new User(1L, tom,Lists.newArrayList(new Info(xxx), new Info(yyy)));//User(id1, nametom, infos[Info(urlxxx), Info(urlyyy)])System.out.println(user);context.setVariable(user, user);String value #{#user.infos[%d].url};String parseValue StringUtils.substring(value, 0, value.indexOf([%d])) };//#{#user.infos}if (!parseValue.equals(value)) {List arrObj parser.parseExpression(parseValue, parserContext).getValue(context, List.class);if (!CollectionUtils.isEmpty(arrObj)) {for (int i 0; i arrObj.size(); i) {String format String.format(value, i);//#{#user.infos[0].url}String oriVal parser.parseExpression(format, parserContext).getValue(context, String.class);log.info(原始集合的值{}, oriVal);//原始集合的值xxx//业务操作重写数据String tarVal oriVal -目标值;//xxx-目标值parser.parseExpression(format, parserContext).setValue(context, tarVal);}}}//User(id1, nametom, infos[Info(urlxxx-目标值), Info(urlyyy-目标值)])System.out.println(user);}
上面案例复杂了 public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();//修改list元素值ListInteger list new ArrayListInteger();list.add(1);list.add(2);EvaluationContext context1 new StandardEvaluationContext();context1.setVariable(collection, list);parser.parseExpression(#collection[1]).setValue(context1, 4);int result1 parser.parseExpression(#collection[1]).getValue(context1, int.class);System.out.println(result1);//4System.out.println(list);// [1, 4]}Map集合
其本质是通过key获得的 public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();//2.测试字典MapString, Integer map new HashMapString, Integer();map.put(a, 1);map.put(b, 2);map.put(c, 3);EvaluationContext context2 new StandardEvaluationContext();context2.setVariable(map, map);System.out.println(parser.parseExpression(#map).getValue(context2));//{a1, b2, c3}System.out.println(parser.parseExpression(#map[b]).getValue(context2));//2MapString, Integer result2 parser.parseExpression(#map.?[key!a]).getValue(context2, Map.class);//{b2, c3}result2.forEach((key, value) - {System.out.println(key : value);});System.out.println(------------);ListInteger result3 parser.parseExpression(#map.?[key!a].![value1]).getValue(context2, List.class);//[3, 4]System.out.println(result3);result3.forEach(System.out::println);}
map修改 public static void main(String[] args) {ExpressionParser parser new SpelExpressionParser();//修改map元素值MapString, Integer map new HashMapString, Integer();map.put(a, 1);EvaluationContext context2 new StandardEvaluationContext();context2.setVariable(map, map);parser.parseExpression(#map[a]).setValue(context2, 4);Integer result2 parser.parseExpression(#map[a]).getValue(context2, int.class);System.out.println(result2);//4}
将上下文对象转换为Map对象 //完整代码在上面List部分哦private static void spEL2Map() {ExpressionParser parser new SpelExpressionParser();EvaluationContext context new StandardEvaluationContext();Account account new Account(Deniro);User user new User(1L, tom, null);//设置上下文对象context.setVariable(account, account);context.setVariable(user, user);//将上下文对象转换为Map对象支持三目表达式#user?.infosMapString, Object objectMap (Map) parser.parseExpression({acName:#account.name,userUrls:#user.infos?.![#this]}).getValue(context);//{acNameDeniro, userUrlsnull}System.out.println(objectMap);}}