一、概述
Spring表示式語言全稱為“Spring Expression Language”,縮寫為“SpEL”。是一個支援查詢,並在執行時操縱一個物件圖功能、是一門強大的表示式語言。SpEL是單獨模組,只依賴於core模組,可以被獨立使用、執行。
Spring 框架元件
二、作用一、基本表示式: 字面量表達式、關係,邏輯與算數運算表示式、字串連線及擷取表示式、三目運算、正則表示式、括號優先順序表示式;
二、類相關表示式: 類型別表示式、類例項化、instanceof表示式、變數定義及引用、賦值表示式、自定義函式、物件屬性存取及安全導航表示式、物件方法呼叫、Bean引用;
三、集合相關表示式: 內聯List、內聯陣列、集合,字典訪問、列表,字典,陣列修改、集合投影、集合選擇;不支援多維內聯陣列初始化;不支援內聯字典定義;
四、其他表示式:模板表示式。
三、主要類1、ExpressionParser
表示式解析器介面,包含了(Expression) parseExpression(String), (Expression) parseExpression(String, ParserContext)兩個介面方法
2、ParserContext
解析器上下文介面,主要是對解析器Token的抽象類,包含3個方法:getExpressionPrefix,getExpressionSuffix和isTemplate,就是表示表示式從什麼符號開始什麼符號結束,是否是作為模板(包含字面量和表示式)解析。
3、Expression
表示式的抽象,是經過解析後的字串表示式的形式表示。透過expressionInstance.getValue方法,可以獲取表示式的值。也可以透過呼叫getValue(EvaluationContext),從評估(evaluation)上下文中獲取表示式對於當前上下文的值
4、EvaluationContext
估值上下文介面,只有一個setter方法:setVariable(String, Object),透過呼叫該方法,可以為evaluation提供上下文變數
四、案例運用一、基礎的Hello
1.1、程式碼實現
@Testpublic void baseTest() {// 字串表示式 String exp = "Hello , #{ #username }"; // 表示式解析器 ExpressionParser parser = new SpelExpressionParser(); // 表示式上下文 EvaluationContext context = new StandardEvaluationContext(); context.setVariable("username", "紋銀三百兩"); // 解析 Expression expression = parser.parseExpression(exp, new TemplateParserContext()); System.out.println(expression.getValue(context, String.class)); }
基礎結果:
Hello , 紋銀三百兩
1.2、分析解析
建立解析器:SpEL使用ExpressionParser介面表示解析器,提供SpelExpressionParser預設實現;解析表示式:使用ExpressionParser的parseExpression來解析相應的表示式為Expression物件。構造上下文:準備比如變數定義等等表示式需要的上下文資料。求值:透過Expression介面的getValue方法根據上下文獲得表示式值。解析表示式示意圖
二、關係運算符
//trueboolean trueValue1 = parser.parseExpression("2 == 2").getValue(Boolean.class);//falseboolean falseValue1 = parser.parseExpression("2 < -5.0").getValue(Boolean.class);//trueboolean trueValue2 = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);//false,字元xyz是否為int型別boolean falseValue2 = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class);//true,正則是否匹配boolean trueValue3 =parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);//falseboolean falseValue3=parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
三、邏輯運算子
// -- AND 與運算 --//false boolean falseValue4 = parser.parseExpression("true and false").getValue(Boolean.class); // -- OR 或運算--//trueboolean trueValue5 = parser.parseExpression("true or false").getValue(Boolean.class);//falseboolean falseValue5 = parser.parseExpression("!true").getValue(Boolean.class);
四、算術運算子
// Additionint two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2String testString =parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string'// Subtractionint four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000// Multiplicationint six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0// Divisionint minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0// Modulusint three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1// Operator precedenceint minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // -21
五、組合使用
@Test public void expressionTest() { String exp = "1 between {1, 2} and 1>2"; ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression(exp); //false System.out.println(expression.getValue(boolean.class)); }
六、操作類
6.1、類型別
@Testpublic void classTypeTest() { ExpressionParser parser = new SpelExpressionParser(); //java.lang包類訪問 Class<String> result1 = parser.parseExpression("T(String)").getValue(Class.class); //class java.lang.String System.out.println(result1); //其他包類訪問 String expression2 = "T(spel.SpElTest)"; Class<SpElTest> value = parser.parseExpression(expression2).getValue(Class.class); //true System.out.println(value == SpElTest.class); //類靜態欄位訪問 int result3 = parser.parseExpression("T(Integer).MAX_VALUE").getValue(int.class); //true System.out.println(result3 == Integer.MAX_VALUE); //類靜態方法呼叫 int result4 = parser.parseExpression("T(Integer).parseInt('1')").getValue(int.class); //1 System.out.println(result4); }
6.2、自定義函式
/** * 兩數之和 */public static Integer add(Integer x, Integer y) { return x + y; }@Testpublic void functionTest() throws NoSuchMethodException { // 表示式 String exp = "#{ #add(4,5)}"; // 表示式上下文 StandardEvaluationContext context = new StandardEvaluationContext(); Method add = SpElTest.class.getDeclaredMethod("add", Integer.class, Integer.class); context.registerFunction("add", add); // 表示式解析器 ExpressionParser parser = new SpelExpressionParser(); // 解析 Expression expression = parser.parseExpression(exp, new TemplateParserContext()); // 9 System.out.println(expression.getValue(context, Integer.class)); }
6.3、類屬性
@Test public void assignTest() { String exp = "username: #{#user.username},age: #{#user.age}"; StandardEvaluationContext context = new StandardEvaluationContext(); Person person = new Person() .setUsername("紋銀三百兩") .setAge(23); context.setVariable("user", person); ExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression(exp, new TemplateParserContext()); //username: 紋銀三百兩,age: 23 System.out.println(expression.getValue(context, String.class)); }
七、模板表示式
@Testpublic void templateTest() { SpelExpressionParser parser = new SpelExpressionParser(); ParserContext context = new TemplateParserContext("%{", "}"); Expression expression = parser.parseExpression("你好:%{#name},正在學習:%{#lesson},加油、奮鬥!!!", context); EvaluationContext evaluationContext = new StandardEvaluationContext(); evaluationContext.setVariable("name", "紋銀三百兩"); evaluationContext.setVariable("lesson", "spring高手系列。"); String value = expression.getValue(evaluationContext, String.class); //你好:紋銀三百兩,正在學習:spring高手系列。加油、奮鬥!!! System.out.println(value); }
八、規則引擎
8.1、背景
假設人員註冊資訊(姓名、年齡、性別),自定義其中規則,如下:
李家好漢(李姓,男,且滿18歲)豆蔻少女(13-15歲,女性)8.2、實現
@Test public void ruleTest() { Person person1 = new Person().setUsername("小龍女").setAge(14).setSex(1); checkRule(FastJsonUtil.parseMap(JSON.toJSONString(person1))); Person person2 = new Person().setUsername("張三").setAge(18).setSex(0); checkRule(FastJsonUtil.parseMap(JSON.toJSONString(person2))); Person person3 = new Person().setUsername("李四").setAge(20).setSex(0); checkRule(FastJsonUtil.parseMap(JSON.toJSONString(person3))); } /** * 規則check * * @param exp 引數map */ private static void checkRule(Map<String, Object> exp) { ExpressionParser parser = new SpelExpressionParser(); //規則容器 Map<String, String> ruleMap = Maps.newHashMap(); String rule1 = "( #username.contains({'李'}) and #age > 18 and #sex == 0 )"; ruleMap.put("李家好漢", rule1); String rule2 = "( #age between {13,15} and #sex == 1 )"; ruleMap.put("豆蔻少女", rule2); EvaluationContext spElContext = getSpElContext(exp); ruleMap.keySet().forEach(key -> { String ruleV = ruleMap.get(key); Boolean isPass = parser.parseExpression(ruleV).getValue(spElContext, Boolean.class); if (Objects.nonNull(isPass) && isPass) { System.out.println("username:【" + exp.get("username") + "】,命中規則:【" + key+"】"); } }); } /** * 解析表示式需要的上下文,透傳請求引數 * * @param param 引數 * @return 返回結果 */ private static EvaluationContext getSpElContext(Map<String, Object> param) { StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); for (Entry<String, Object> entry : param.entrySet()) { if (entry.getValue() != null) { evaluationContext.setVariable(entry.getKey(), entry.getValue()); } } return evaluationContext; }
結果:
username:【小龍女】,命中規則:【豆蔻少女】username:【李四】,命中規則:【李家好漢】
五、總結
Spring EL表示式,作為JAVA的內建語言,十分強大。主要可以用來做表示式解析,或者規則鏈路,且可以操作函式方法;從而達到一種動態的鏈路規則解析效果。
參考文章:https://docs.spring.io/spring-integration/docs/5.3.0.RELEASE/reference/html/spel.html#spel