Spring SpEL
文章目錄
- Spring SpEL
-
- 一、JVM動态語言
- 二、SpEL表達式
- 三、SpEL的核心接口
-
- 3.1 `ExpressionParser`
-
- (1) 字元串的拼接
- 3.2 `EvaluationContext`
-
- (2) 針對特定執行個體對象的屬性進行求值
- 3.3 `SpelCompiler`可以将位元組碼直接編譯成位元組碼
- 四、SpEL表達式
-
- 4.1 文本字元解析
- 4.2 對象屬性解析
- 4.3 數組、集合類型解析
- 4.4 方法解析
- 4.5 操作符解析
-
- (1) 關系操作符
- (2)邏輯操作符
- (3)算術運算操作符
- 4.6 安全導航運算符(避免了空指針對象驗證)
- 4.7 三元運算符
- 4.8 `Elvis`運算符
- 4.9 指派、類型、構造器、變量
- 4.10 集合過濾
- 4.11 集合轉換
- 五、在Spring中使用SpEL
-
- 5.1 基于XML配置
- 5.2 基于注解的配置
一、JVM動态語言
作用:表達式語句的動态解析。
項目中的實踐:在項目開發中,遇到一種場景,需要根據公式計算資料,而涉及到很多公式,并且公式可能會有變動。是以,在開發中我就使用到了動态解析這種方案——将計算公式存到表中。能夠非常靈活地計算出結果。
Java6.0 中内置內建了Mozila Rhino的JavaScript解析引擎,是以可以很友善地在Java中調用JavaScript函數。
動态求和函數:
public static void main(String[] args) throws ScriptException, NoSuchMethodException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
String scriptText = "function sum(a,b){ return a+b;}";
engine.eval(scriptText);
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("sum", 100, 99);
System.out.println(result instanceof Double);
System.out.println(result);
}
二、SpEL表達式
對于僅僅需要一些簡單的表達式需求的場景,使用腳本語言顯得有些“笨重”,這就是介紹SpEL表達式的原因。
- SpEL具有顯式方法調用和基本字元串模闆函數等特性;
- SpEL不直接依賴于Spring架構,可以獨立使用;
可以将SpEL作為獨立的動态語言使用
使用前需要引入
spring-expression
:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("'hello' + ' world'");
String message= (String) expression.getValue();
System.out.println(message);
}
三、SpEL的核心接口
3.1 ExpressionParser
ExpressionParser
(1) 字元串的拼接
private static void testStrConcat(){
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("'HelloWorld'.concat('!')");
String value = (String)expression.getValue();
System.out.println(value);
System.out.println(expression.getValue(String.class));
}
3.2 EvaluationContext
EvaluationContext
(2) 針對特定執行個體對象的屬性進行求值
建立
StandardEvaluationContext
執行個體時,指定一個跟對象作為求值的目标對象。
private static void objPropertiesValue(){
User user = new User();
user.setUserName("XiaoMing");
user.setCredits(100);
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext(user);
Object userName = parser.parseExpression("userName").getValue(context);
System.out.println(userName);
}
3.3 SpelCompiler
可以将位元組碼直接編譯成位元組碼
SpelCompiler
- 由于
直接将表達式編譯成位元組碼,避免了每次調用時的文法解析消耗SpelCompiler
public static void main(String[] args) {
User user = new User();
// 建立解析配置
SpelParserConfiguration configuration = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
CompilerSample.class.getClassLoader());
// 建立解析器
SpelExpressionParser parser = new SpelExpressionParser(configuration);
// 建立擷取上下文
EvaluationContext context = new StandardEvaluationContext(user);
// 表達式
String expression = "isVipMember('tom') && isVipMember('pip')";
// 解析表達式
Expression spELExpression = parser.parseRaw(expression);
// 通過表達式求值
System.out.println(spELExpression.getValue(context));
System.out.println(spELExpression.getValue(context));
}
四、SpEL表達式
4.1 文本字元解析
文本表達式支援字元串、日期、數字(正數、實數及十六進制數)、布爾類型及null。
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
String helloWorld = parser.parseExpression("'Hello World'").getValue(String.class);
double doubleNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
System.out.println(doubleNumber);// 6.0221415E23
}
4.2 對象屬性解析
使用類似“xxx.yyy.zzz”的對象屬性路徑輕松通路對象屬性值。
4.3 數組、集合類型解析
在SpEL中,支援數組、集合類型(Map、List)的解析。
4.4 方法解析
- 支援Java可通路方法。私有方法是不能調用的;
- 可以調用String類型的所有可通路的方法;
4.5 操作符解析
(1) 關系操作符
> >= < <= ==
、正規表達式、
instanceof
操作符
(2)邏輯操作符
and 或 &&
、
or 或 ||
、非操作
!
(3)算術運算操作符
- 加法運算可用于數字、字元串、日期;
- 減法運算可用于數字和日期;
- 乘法和除法運算符僅用于數字;
- 還支援
、指數幂%
^
4.6 安全導航運算符(避免了空指針對象驗證)
格式為在“.”前面添加一個“?”
如果user為空,則直接傳回null,如果不為空,則擷取屬性值。
"user?.userName"
4.7 三元運算符
public static void main(String[] args) {
User user = new User();
user.setUserName("Tom");
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext(user);
String result = parser.parseExpression("userName=='Tom'?'hello '+userName:'not Tom'").getValue(context, String.class);
System.out.println(result);
}
4.8 Elvis
運算符
Elvis
<var>?:<value>
如果左邊變量取值為null,就取value值,否則就取var變量自身的值。
user.setUserName(null);
String elvisValue = parser.parseExpression("userName?:'empty Username'").getValue(context, String.class);
System.out.println(elvisValue);
4.9 指派、類型、構造器、變量
指派
public static void main(String[] args) {
User user = new User();
user.setUserName("Tom");
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext(user);
// 通過 setValue指派
parser.parseExpression("userName").setValue(context, "XiaoMing");
System.out.println(user.getUserName());
// 通過 表達式指派
parser.parseExpression("userName='xiaoHong'").getValue(context);
System.out.println(user.getUserName());
}
類型
- 如果加載的目标類位于java.lang包下,則可以不帶包名;
- T操作符還可以直接調用類靜态方法;
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
Class stringClass = parser.parseExpression("T(java.lang.String)").getValue(Class.class);
System.out.println(stringClass == String.class);
// 直接調用靜态方法
Object randomValue = parser.parseExpression("T(java.lang.Math).random()").getValue();
System.out.println(randomValue);
}
構造器
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
User value = parser.parseExpression("new com.hef.beans.User('aaa')").getValue(User.class);
System.out.println(value);
}
變量
// 設定一個變量
EvaluationContext context = new StandardEvaluationContext(value);
context.setVariable("oneUserName", "Tom");
parser.parseExpression("userName=#oneUserName").getValue(context);
System.out.println(value.getUserName());
4.10 集合過濾
public static void main(String[] args) {
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext();
// 過濾list
List<Integer> valueList = new ArrayList<>();
valueList.addAll(Arrays.asList(2,3,6,10,5,11,13));
context.setVariable("list", valueList);
List<Integer> value = (List<Integer>) parser.parseExpression("#list.?[#this>5]").getValue(context);
System.out.println(value);
// 過濾map
Map<String, Integer> map = new HashMap<>();
map.put("aa", 23);
map.put("bb", 12);
map.put("cc", 6);
context.setVariable("map", map);
// 過濾出子集
Map<String, Integer> map1= (Map<String, Integer>) parser.parseExpression("#map.?[value>7]").getValue(context);
System.out.println(map1);
// 取第一個值
Map<String, Integer> map2= (Map<String, Integer>) parser.parseExpression("#map.^[value>7]").getValue(context);
System.out.println(map2);
// 取最後一個值
Map<String, Integer> map3= (Map<String, Integer>) parser.parseExpression("#map.$[value>7]").getValue(context);
System.out.println(map3);
}
4.11 集合轉換
// 集合的轉換
List<Boolean> booleanList= (List<Boolean>) parser.parseExpression("#list.![#this>10]").getValue(context);
System.out.println(booleanList);
五、在Spring中使用SpEL
5.1 基于XML配置
5.2 基于注解的配置
#{properties['password']}