java利用JEXL實(shí)現(xiàn)動(dòng)態(tài)表達(dá)式編譯
背景
做項(xiàng)目突然遇到這樣的需求:
系統(tǒng)要獲取多個(gè)數(shù)據(jù)源的數(shù)據(jù),并進(jìn)行處理,最后輸出多個(gè)字段。字段的計(jì)算規(guī)則一般是簡(jiǎn)單的取值最多加一點(diǎn)條件判斷。
而且需要?jiǎng)討B(tài)變動(dòng)??!例如一個(gè)字段a的取值,如果a > 10的時(shí)候輸出10,a <= 10則輸出a。這里的10可能在一天后改成8,也可能在后天就改成了12。當(dāng)然,如果只是一個(gè)數(shù)字的變動(dòng)還好說(shuō),我們可以使用數(shù)據(jù)庫(kù)進(jìn)行存儲(chǔ)。但是,萬(wàn)一哪天需求突然變成了a < 10的時(shí)候輸出10,a >=10 則輸出a,就需要對(duì)代碼改動(dòng),再測(cè)試再發(fā)布才能到生產(chǎn)環(huán)境使用。
一兩個(gè)這樣的字段還沒(méi)什么,如果整個(gè)系統(tǒng)所依賴(lài)的字段都有這樣的屬性,那么我們就需要找一種方法來(lái)實(shí)現(xiàn)動(dòng)態(tài)的加載邏輯。
下面介紹的JEXL就可以解決這種問(wèn)題
JEXL(Java Expression Language)介紹
JEXL – Apache Commons JEXL Overview
下面用一些實(shí)例來(lái)介紹JEXL的使用方法
實(shí)例
maven依賴(lài):
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl</artifactId>
<version>2.0</version>
</dependency>
正則表達(dá)式匹配
首先寫(xiě)一個(gè)公共方法:
public class Util {
public static boolean regMatch(String regEx, String str) {
Pattern pattern = Pattern.compile(regEx);
return pattern.matcher(str).matches();
}
}
下面是使用JEXL調(diào)用的方法
public void RL() {
JexlContext jc = new MapContext();
String str = "一二三四五六七八九十";
jc.set("Util", new Util());
jc.set("str", str);
jc.set("ans", "");
String expression = "ans = Util.regMatch(\"[\u4e00-\u9fa5]{10,}\",str)";
Expression e = new JexlEngine().createExpression(expression);
e.evaluate(jc);
System.out.println(jc.get("ans"));
}
代碼中的expression變量就是可以動(dòng)態(tài)編譯的表達(dá)式,這里要注意表達(dá)式中出現(xiàn)的所有變量,都需要事先set進(jìn)JexlContext中,否則會(huì)報(bào)錯(cuò)。這里有多種形式的錯(cuò)誤:
①如果沒(méi)有set”Util”,程序運(yùn)行中會(huì)拋出異常。
org.apache.commons.jexl2.JexlException: TmpTest.RL@40![13,40]: 'ans = QeUtil.regMatch('[一-龥](méi){10,}', str);' attempting to call method on null
②如果沒(méi)有set”str”,程序不會(huì)拋出異常,并輸出null。如果你的regMatch方法中有判空處理,就會(huì)輸出判空的結(jié)果。如果沒(méi)有判空處理,在控制臺(tái)的輸出如下:
警告: TmpTest.RL@39![36,39]: 'ans = QeUtil.regMatch('[一-龥](méi){10,}', str);' undefined variable str
二月 21, 2017 4:00:41 下午 org.apache.commons.jexl2.JexlEngine invocationFailed
警告: TmpTest.RL@39![13,40]: 'ans = QeUtil.regMatch('[一-龥](méi){10,}', str);' method invocation error
java.lang.NullPointerException
③如果沒(méi)有set”ans”,程序會(huì)正常運(yùn)行,并輸出正確值
為了保險(xiǎn)起見(jiàn),建議表達(dá)式中出現(xiàn)的所有變量,都需要事先set進(jìn)JexlContext中
循環(huán)
JEXL支持兩種循環(huán)方式:
for(item : list) {
x = x + item;
}
和
while (x lt 10) {
x = x + 2;
}
下面是使用while的實(shí)例:
public void loop() {
JexlContext jc = new MapContext();
jc.set("a", 1);
jc.set("b", "0");
jc.set("ans", new StringBuffer());
Expression e = new JexlEngine().createExpression("while (a < 10) {a = a + 1;ans.append(b);}");
e.evaluate(jc);
System.out.println(jc.get("ans"));
}
get\set方法調(diào)用
JEXL支持傳入對(duì)象,并調(diào)用對(duì)象的方法
下面的簡(jiǎn)單的get\set方法的實(shí)例:
public void getSet() {
TmpTest tmpTest = new TmpTest();
tmpTest.setA(1);
JexlContext jc = new MapContext();
jc.set("tmpTest", tmpTest);
jc.set("ans", "");
Expression e = new JexlEngine().createExpression("ans = tmpTest.getA()");
e.evaluate(jc);
System.out.println(jc.get("ans"));
e = new JexlEngine().createExpression("ans = tmpTest.setA(2)");
e.evaluate(jc);
TmpTest tmpTest1 = (TmpTest) jc.get("tmpTest");
System.out.println(tmpTest1.getA());
}
上面的用例會(huì)在控制臺(tái)先輸出1,再輸出2
下面是其他網(wǎng)友的評(píng)論
ScriptEngine比這個(gè)方便n倍,能動(dòng)態(tài)執(zhí)行js function,了解一下
相關(guān)文章
springboot + devtools(熱部署)實(shí)例教程
devtools是boot的一個(gè)熱部署工具,當(dāng)我們修改了classpath下的文件(包括類(lèi)文件、屬性文件、頁(yè)面等)時(shí),會(huì)重新啟動(dòng)應(yīng)用。本文通過(guò)實(shí)例給大家介紹springboot+devtools熱部署,感興趣的朋友一起看看吧2017-04-04
Java類(lèi)加載器之ContextClassLoader詳解
這篇文章主要介紹了Java類(lèi)加載器之ContextClassLoader詳解,ContextClassLoader是一種與線程相關(guān)的類(lèi)加載器,類(lèi)似ThreadLocal,每個(gè)線程對(duì)應(yīng)一個(gè)上下文類(lèi)加載器,需要的朋友可以參考下2023-10-10
JVM性能調(diào)優(yōu)實(shí)戰(zhàn):讓你的IntelliJ Idea縱享絲滑
這篇文章主要介紹了JVM性能調(diào)優(yōu)實(shí)戰(zhàn):讓你的IntelliJ Idea縱享絲滑的相關(guān)資料,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
老生常談spring boot 1.5.4 日志管理(必看篇)
下面小編就為大家?guī)?lái)一篇老生常談spring boot 1.5.4 日志管理(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06
關(guān)于SpringBoot2.7.6連接nacos遇到的一些問(wèn)題
這篇文章主要介紹了關(guān)于SpringBoot2.7.6連接nacos遇到的一些問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
java中@EnableAutoConfiguration注解使用
在Spring Boot框架中,@EnableAutoConfiguration是一種非常重要的注解,本文就來(lái)介紹一下java中@EnableAutoConfiguration注解使用,感興趣的可以了解一下2023-11-11
SpringBoot+MyBatis實(shí)現(xiàn)MD5加密數(shù)據(jù)庫(kù)用戶密碼的方法
MD5技術(shù)主要用于對(duì)用戶密碼加密,增加賬戶的安全性,他具有不可逆的特性,不會(huì)被輕易解密,這篇文章給大家介紹SpringBoot+MyBatis實(shí)現(xiàn)MD5加密數(shù)據(jù)庫(kù)用戶密碼的方法,感興趣的朋友跟隨小編一起看看吧2024-03-03
Java實(shí)現(xiàn)的獲取和判斷文件頭信息工具類(lèi)用法示例
這篇文章主要介紹了Java實(shí)現(xiàn)的獲取和判斷文件頭信息工具類(lèi),結(jié)合實(shí)例形式分析了Java針對(duì)文件讀取及頭信息判斷相關(guān)操作技巧,需要的朋友可以參考下2017-11-11

