1. 程式人生 > 實用技巧 >Jexl表示式引擎-根據字串動態執行JAVA.md

Jexl表示式引擎-根據字串動態執行JAVA.md


Table of Contents generated with DocToc

一、使用場景

在做某些專案的時候,有時會遇到如下情景:

使用者需要傳入某個JAVA 表示式,然後後臺將這個表示式當作JAVA程式碼執行


二、市面上表示式引擎比較

我們有許多表達式引擎可供選擇:

  • Jexl
  • Aviator

2.1 Aviator

avitor具體使用技巧可以參考這篇部落格:https://blog.csdn.net/keda8997110/article/details/50782848

avitor可以滿足基本的表示式的判斷,但對於物件中的函式呼叫明顯力不從心,如果碰到以下業務情景:

使用者傳入的java表示式是:user.getName().equals("123456");

這個表示式是呼叫user物件裡的getName方法,然後再掉用equals方法,判斷是否等於字串123456,avitor對於呼叫物件的方法這個問題也有自己的解決方法:

User user = new User("123456");

//首先要注入User的類
AviatorEvaluator.addInstanceFunctions("user", User.class); //再使用aviator特定的表示式執行方法: namespace.method(instance, args)
AviatorEvaluator.execute("User.getName(user)=='123456'")

這樣太麻煩了,雖然程式可以事先將需要的類注入到AviatorEvaluator引擎中,但是我們還需要將熟悉的java表示式轉化為Aviator需要的表示式形式。

那我們有沒有可以直接執行java程式碼的呢?有!

2.2 Jexl

我們在maven中使用 Jexl 需要匯入如下依賴:

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-jexl -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl</artifactId>
<version>2.1.1</version>
</dependency>

然後看我們的 Jexl 如何實現業務:

使用者傳入的java表示式是:user.getName().equals("123456");

然後後臺直接執行

  1. 首先我們實現一個方法,用於動態載入函式
//動態載入方法
//jexlExp -- java表示式
//map -- 執行環境
private Object invokeMethod(String jexlExp, Map<String,Object> map){
JexlEngine jexl=new JexlEngine();
Expression e = jexl.createExpression(jexlExp);
JexlContext jc = new MapContext();
for(String key:map.keySet()){
jc.set(key, map.get(key));
}
if(null==e.evaluate(jc)){
return "";
}
return e.evaluate(jc);
}
  1. 然後編寫測試用例:
pubic class User{
String name = "123456";
public User(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public class JexlTest {

    private Map<String, Object> env;

    //注入測試環境,將變數形成一個map<物件名,物件>
@Before
public void before(){
env = new HashMap<>();
env.put("id","123456");
env.put("user",new User());
} //動態載入方法
private Object invokeMethod(String jexlExp, Map<String,Object> map){
JexlEngine jexl=new JexlEngine();
Expression e = jexl.createExpression(jexlExp);
JexlContext jc = new MapContext();
for(String key:map.keySet()){
jc.set(key, map.get(key));
}
if(null==e.evaluate(jc)){
return "";
}
return e.evaluate(jc);
} //呼叫Jexl執行引擎
@Test
public void TestFunctionMethod(){
String expression="user.getId().equals(id)";
boolean res = (boolean) invokeMethod(expression,env);
Assert.assertTrue(res);
}
}

執行結果為true

與Aviator相比,我們省去了修改表示式的步驟