JSR223 Java使用指令碼引擎動態修改業務邏輯

fairjm發表於2016-11-30

本文來自圖靈社群 @fairjm 轉截請註明出處


偶爾會有一些業務需求是需要在動態改變線上程式碼的行為.一般的做法是使用個配置檔案,存在資料庫或者redis等儲存中,程式動態獲取之後解析並根據配置進行相應的操作.

在配置不復雜的情況下這樣的做法能夠滿足需求.但如果配置很複雜,或者配置的規則很多,那麼解析配置並執行就變成了一件很麻煩的事情.可能會引入一些解析器,或者Criteria這樣的類.

如果這樣,那是不是嵌入一段程式碼動態載入執行會不會簡單點?
好在java本身就提供了這樣的機制,也就是JSR-223.

因為是開發使用的,所以最好還是用和java一樣的語法,方便現有開發,那選取的指令碼語言為groovy比較合適,畢竟能相容java語法.

搞起.
首先引入依賴:

    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-jsr223</artifactId>
        <version>2.4.7</version>
    </dependency>

這邊用判斷使用者是否能夠訪問作為例子.

其中要考慮的使用者類如下(省略getter setter和構造器):

public class User {

    private String userName;

    private Long userId;

    private Boolean isVip;

    private Integer gender;

    private Integer age;  

現在的規則是如果是非生產環境返回true,會員返回true,使用者以test開頭(這只是例子 千萬別學)返回true 如果性別是男性並且大於18歲返回true(咦)

對應的groovy指令碼如下:

if (!prod) {
    return true;
}
if (Boolean.TRUE.equals(user.getIsVip())) {
    return true;
}
if (user.getUserName()?.startsWith("test")) {
    return true;
}
if (user.getGender()?.equals(1) && user.getAge() != null && user.getAge() >= 18) {
    return true;
}
return false;

?表示式可以減少空判斷,因為groovy相容java,所以手工寫 != null(比如age)也是可以的.

接來下執行:

// 獲取指令碼引擎
final ScriptEngineManager factory = new ScriptEngineManager();
final ScriptEngine engine = factory.getEngineByName("groovy");
// 讀取指令碼
String str;
try (BufferedReader reader = new BufferedReader(
        new InputStreamReader(
                GroovyScriptTest.class.getResourceAsStream("/groovy-script")))) {
    str = reader.lines().collect(Collectors.joining("\r\n"));
}
final User u = new User("fairjm", 1L, false, 1, 18);
// groovy的引擎是支援可編譯的所以直接這麼寫
final CompiledScript script = ((Compilable) engine).compile(str);
final Bindings bindings = new SimpleBindings();
bindings.put("prod", true);
bindings.put("user", u);
final Boolean result = (Boolean) script.eval(bindings);
System.out.println(result);

返回true.

以上,如果從寫配置檔案的角度想,上面那麼幾個條件以及條件的優先順序和組合會比較麻煩(感興趣的可以試試看).

參考了
http://www.ticmy.com/?p=267
原文對於JSR223說得比這文更詳細,可以去讀一下.

相關文章