Files
2023-06-29 20:16:26 +08:00

605 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

- [Spel](#spel)
- [Spel表达式计算](#spel表达式计算)
- [示例](#示例)
- [Concept](#concept)
- [调用方法](#调用方法)
- [调用属性](#调用属性)
- [调用构造器](#调用构造器)
- [在特定对象上计算表达式](#在特定对象上计算表达式)
- [EvaluationContext](#evaluationcontext)
- [EvaluationContext实现类](#evaluationcontext实现类)
- [TypeConversion](#typeconversion)
- [在定义Bean对象时使用Spel表达式](#在定义bean对象时使用spel表达式)
- [为field指定默认值](#为field指定默认值)
- [通过setter为field指定默认值](#通过setter为field指定默认值)
- [@Autowired方法和构造器](#autowired方法和构造器)
- [Spel语法](#spel语法)
- [字面量表达式](#字面量表达式)
- [字符串类型字面量](#字符串类型字面量)
- [Properties,Array,List,Map,Indexer](#propertiesarraylistmapindexer)
- [访问属性](#访问属性)
- [Array](#array)
- [Map](#map)
- [列表表示](#列表表示)
- [数组构建](#数组构建)
- [方法调用](#方法调用)
- [操作符](#操作符)
- [关系操作符](#关系操作符)
- [instanceof操作符和matches操作符](#instanceof操作符和matches操作符)
- [逻辑操作符](#逻辑操作符)
- [数学运算符](#数学运算符)
- [赋值运算符](#赋值运算符)
- [类型操作符](#类型操作符)
- [构造方法](#构造方法)
- [变量](#变量)
- [#this and #root](#this-and-root)
- [方法注册](#方法注册)
- [引用bean对象](#引用bean对象)
- [三元操作符](#三元操作符)
- [Elvis Operator](#elvis-operator)
- [空安全操作符](#空安全操作符)
- [集合过滤操作符](#集合过滤操作符)
- [集合映射](#集合映射)
- [表达式模板](#表达式模板)
# Spel
## Spel表达式计算
### 示例
如下是一个Spel表达式计算示例
```java
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'");
String message = (String) exp.getValue();
// 最终message计算的值是'Hello World'
```
### Concept
`ExpressionParser`接口用于对表达式字符串进行解析,在上述示例中,表达式字符串是一个由单引号包围起来的字符串字面量。
`Expression`接口则是负责对表达式字符串进行计算。
在调用`parser.parseExpression``exp.getValue`,分别会抛出`ParseException``EvaluationException`异常。
Spel支持许多特性例如调用方法访问属性调用构造方法等示例如下
#### 调用方法
```java
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue();
// message值为Hello World!
```
#### 调用属性
```java
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes().length'
Expression exp = parser.parseExpression("'Hello World'.bytes.length");
int length = (Integer) exp.getValue();
```
#### 调用构造器
```java
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()");
String message = exp.getValue(String.class);
```
通过`public <T> T getValue(Class<T> desiredResultType)`方法向getValue方法传递一个Class对象可以避免在调用getValue方法调用之后需要将返回值手动转化为特定类型。如果类型转化失败会抛出一个EvaluationException异常。
#### 在特定对象上计算表达式
Spel支持在一个特定的对象上该对象通常被称作root object进行计算例如如下示例
```java
// 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
```
### EvaluationContext
EvalutaionContext用于给表达式提供一个执行的上下文环境通过EvalutaionContext可以向context中放置一个变量并且在后续中使用该变量
```java
// 向context中放入一个list变量
ctx.setVariable("list",list)
// 在后续表达式中即可使用放入的list变量
// 获取放入context中的list变量的值
parser.parseExpression("#list[0]").getValue(ctx);
// 设置context中list变量的值
parser.parseExpression("#list[0]").setValue(ctx , "false");
```
#### EvaluationContext实现类
- SimpleEvaluationContext实现了Spel语言的部分特性
- StandardEvaluationContext实现了Spel语言的全部特性
### TypeConversion
默认情况下spel会使用`org.springframework.core.convert.ConversionService`中的Conversion Service。该conversion service包含内置的类型转换并且可以为类型之间指定自定义的类型转换。该转换是支持泛型的。
如下为支持泛型的示例:
```java
class Simple {
public List<Boolean> booleanList = new ArrayList<>();
}
Simple simple = new Simple();
simple.booleanList.add(true);
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
// "false" is passed in here as a String. SpEL and the conversion service
// will recognize that it needs to be a Boolean and convert it accordingly.
parser.parseExpression("booleanList[0]").setValue(context, simple, "false");
// b is false
Boolean b = simple.booleanList.get(0);
```
### 在定义Bean对象时使用Spel表达式
可以通过@Value注解来为bean对象的域、方法参数、构造器参数指定默认值。通过如下方式来指定表达式:
- `#{ <expression string> }.`
#### 为field指定默认值
```java
public class FieldValueTestBean {
@Value("#{ systemProperties['user.region'] }")
private String defaultLocale;
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
```
#### 通过setter为field指定默认值
```java
public class PropertyValueTestBean {
private String defaultLocale;
@Value("#{ systemProperties['user.region'] }")
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
```
#### @Autowired方法和构造器
@Autowired方法和构造器可以使用@Value注解为参数指定默认值
```java
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String defaultLocale;
@Autowired
public void configure(MovieFinder movieFinder,
@Value("#{ systemProperties['user.region'] }") String defaultLocale) {
this.movieFinder = movieFinder;
this.defaultLocale = defaultLocale;
}
// ...
}
```
```java
public class MovieRecommender {
private String defaultLocale;
private CustomerPreferenceDao customerPreferenceDao;
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
@Value("#{systemProperties['user.country']}") String defaultLocale) {
this.customerPreferenceDao = customerPreferenceDao;
this.defaultLocale = defaultLocale;
}
// ...
}
```
## Spel语法
### 字面量表达式
字面量表达式支持字符串、数值整数、实数、十六进制数、boolean类型和null。
#### 字符串类型字面量
其中字符串类型的字面量通过单引号包围,如果想要将单引号本身放入字符串字面量中,可以使用两个单引号:
```java
// 该值为h''h
parser.parseExpression("'h''''h'").getValue()
```
字面量的使用如下所示:
```java
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();
```
### Properties,Array,List,Map,Indexer
#### 访问属性
```java
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
```
属性名的第一个字符不区分大小写。
#### Array
可以通过方括号来访问array和list中的元素内容
```java
ExpressionParser parser = new SpelExpressionParser();
// Inventions Array
StandardEvaluationContext teslaContext = new StandardEvaluationContext(tesla);
// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(teslaContext,
String.class);
// Members List
StandardEvaluationContext societyContext = new StandardEvaluationContext(ieee);
// evaluates to "Nikola Tesla"
String name = parser.parseExpression("Members[0].Name").getValue(societyContext, String.class);
// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext,String.class);
```
#### Map
map中的值可以在方括号中指定字面量key来访问示例如下所示
```java
// Officer's Dictionary
Inventor pupin = parser.parseExpression("Officers['president']").getValue(societyContext,Inventor.class);
// evaluates to "Idvor"
String city =
parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(societyContext,
String.class);
// setting values
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext,"Croatia");
```
#### 列表表示
列表可以直接通过如下方式来表示:
```java
// evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
```
#### 数组构建
spel表达式中数组构建可以使用java语法
```java
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
// Array with initializer
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);
// Multi dimensional array
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
```
### 方法调用
表达式中方法调用也可以使用java中的语法
```java
// string literal, evaluates to "bc"
String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class);
// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class);
```
### 操作符
#### 关系操作符
关系操作符示例如下:
```java
// evaluates to true
boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);
// evaluates to false
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
// evaluates to true
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
```
与null比较时的规则
- 任何值都比null大`X>null`永远为true
- 没有任何值比null小`X<null`永远为false
#### instanceof操作符和matches操作符
```java
// evaluates to false
boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);
// evaluates to true
boolean trueValue =
parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
//evaluates to false
boolean falseValue =
parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
```
#### 逻辑操作符
逻辑操作符支持and、or、not
```java
// -- AND --
// evaluates to false
boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);
// evaluates to true
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
// -- OR --
// evaluates to true
boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);
// evaluates to true
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
// -- NOT --
// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);
// -- AND and NOT --
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
```
#### 数学运算符
```java
// Addition
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
String testString =
parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string'
// Subtraction
int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000
// Multiplication
int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0
// Division
int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0
// Modulus
int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3
int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1
// Operator precedence
int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
```
#### 赋值运算符
```java
Inventor inventor = new Inventor();
StandardEvaluationContext inventorContext = new StandardEvaluationContext(inventor);
parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2");
// alternatively
String aleks = parser.parseExpression("Name = 'Alexandar Seovic'").getValue(inventorContext,
String.class);
```
#### 类型操作符
T操作符会获取一个class对象在传入T的类型为java.lang下类时不用指定全类名否则应指定全类名.
类中的静态方法也可以通过该操作符进行调用:
```java
Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
boolean trueValue =
parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR")
.getValue(Boolean.class);
```
#### 构造方法
可以通过new操作符来调用构造方法在调用构造方法需要指定全类名但是基类和String可以不指定全类名
```java
Inventor einstein =
p.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein',
'German')")
.getValue(Inventor.class);
//create new inventor instance within add method of List
p.parseExpression("Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein',
'German'))")
.getValue(societyContext);
```
### 变量
可以通过`#argName`语法来引用变量,可以通过`StandardEvaluationContext.setVariable`方法来设置变量:
```java
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
context.setVariable("newName", "Mike Tesla");
parser.parseExpression("Name = #newName").getValue(context);
System.out.println(tesla.getName()) // "Mike Tesla"
```
#### #this and #root
#this变量一直都是被定义的用于引用当前评估对象#root变量也是一直都被定义的引用root context对象。
```java
// 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);
```
### 方法注册
可以向StandardEvaluationContext中注册方法注册的方法可以在spel中使用
```java
public abstract class StringUtils {
public static String reverseString(String input) {
StringBuilder backwards = new StringBuilder();
for (int i = 0; i < input.length(); i++)
backwards.append(input.charAt(input.length() - 1 - i));
}
return backwards.toString();
}
}
```
注册方法到context中
```java
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.registerFunction("reverseString",
StringUtils.class.getDeclaredMethod("reverseString",
new Class[] { String.class }));
String helloWorldReversed =
parser.parseExpression("#reverseString('hello')").getValue(context, String.class);
```
### 引用bean对象
如果为context配置了bean resolver可以通过`@`语法来查询bean对象
```java
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@foo").getValue(context);
```
### 三元操作符
```java
parser.parseExpression("Name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");
expression = "isMember(#queryName)? #queryName + ' is a member of the ' " +
"+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";
String queryResultString =
parser.parseExpression(expression).getValue(societyContext, String.class);
// queryResultString = "Nikola Tesla is a member of the IEEE Society"
```
### Elvis Operator
类似于`Optional.ofNullable(var).orElse()`,`?:`表达式可以达成同样效果:
```java
ExpressionParser parser = new SpelExpressionParser();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);
System.out.println(name); // Nikola Tesla
tesla.setName(null);
name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, String.class);
System.out.println(name); // Elvis Presley
```
### 空安全操作符
`?.`操作符和js中的类似
```java
ExpressionParser parser = new SpelExpressionParser();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
StandardEvaluationContext context = new StandardEvaluationContext(tesla);
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
System.out.println(city); // Smiljan
tesla.setPlaceOfBirth(null);
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
System.out.println(city); // null - does not throw NullPointerException!!!
```
### 集合过滤操作符
`?[selectionExpression]`可以完成集合的过滤该操作符可以同时针对list和map在针对map操作时操作的是`Map.Entry`entry的key和value都可以作为属性访问。
```java
// 针对list
List<Inventor> list = (List<Inventor>)
parser.parseExpression("Members.?[Nationality == 'Serbian']").getValue(societyContext);
// 针对map
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
```
### 集合映射
类似于Stream.mapspel支持集合映射操作符
```java
// returns [ 'Smiljan', 'Idvor' ]
// 将Inventor集合映射为city集合
List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");
```
### 表达式模板
表达式模板允许将字面量文本和一个或多个评估component混合在一起评估块语法类似于`${}`
```java
String randomPhrase =
parser.parseExpression("random number is #{T(java.lang.Math).random()}",
new TemplateParserContext()).getValue(String.class);
// evaluates to "random number is 0.7038186818312008"
```