背景
做項目突然遇到這樣的需求:
系統要擷取多個資料源的資料,并進行處理,最後輸出多個字段。字段的計算規則一般是簡單的取值最多加一點條件判斷。
而且需要動态變動!!例如一個字段a的取值,如果a > 10的時候輸出10,a <= 10則輸出a。這裡的10可能在一天後改成8,也可能在後天就改成了12。當然,如果隻是一個數字的變動還好說,我們可以使用資料庫進行存儲。但是,萬一哪天需求突然變成了a < 10的時候輸出10,a >=10 則輸出a,就需要對代碼改動,再測試再釋出才能到生産環境使用。
一兩個這樣的字段還沒什麼,如果整個系統所依賴的字段都有這樣的屬性,那麼我們就需要找一種方法來實作動态的加載邏輯。
下面介紹的JEXL就可以解決這種問題
JEXL(Java Expression Language)介紹
JEXL – Apache Commons JEXL Overview
下面用一些執行個體來介紹JEXL的使用方法
執行個體
maven依賴:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl</artifactId>
<version>2.0</version>
</dependency>
正規表達式比對
首先寫一個公共方法:
public class Util {
public static boolean regMatch(String regEx, String str) {
Pattern pattern = Pattern.compile(regEx);
return pattern.matcher(str).matches();
}
}
下面是使用JEXL調用的方法
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變量就是可以動态編譯的表達式,這裡要注意表達式中出現的所有變量,都需要事先set進JexlContext中,否則會報錯。這裡有多種形式的錯誤:
①如果沒有set”Util”,程式運作中會抛出異常。
②如果沒有set”str”,程式不會抛出異常,并輸出null。如果你的regMatch方法中有判空處理,就會輸出判空的結果。如果沒有判空處理,在控制台的輸出如下:
警告: TmpTest.RL@![,]: 'ans = QeUtil.regMatch('[一-龥]{,}', str);' undefined variable str
二月 , :: 下午 org.apache.commons.jexl2.JexlEngine invocationFailed
警告: TmpTest.RL@![,]: 'ans = QeUtil.regMatch('[一-龥]{,}', str);' method invocation error
java.lang.NullPointerException
③如果沒有set”ans”,程式會正常運作,并輸出正确值
為了保險起見,建議表達式中出現的所有變量,都需要事先set進JexlContext中
循環
JEXL支援兩種循環方式:
for(item : list) {
x = x + item;
}
和
while (x lt ) {
x = x + ;
}
下面是使用while的執行個體:
public void loop() {
JexlContext jc = new MapContext();
jc.set("a", );
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方法調用
JEXL支援傳入對象,并調用對象的方法
下面的簡單的get\set方法的執行個體:
public void getSet() {
TmpTest tmpTest = new TmpTest();
tmpTest.setA();
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());
}
上面的用例會在控制台先輸出1,再輸出2