springboot安全使用groovyshell

最近在公司寫實時核對系統,其中在核對環節用到了groovy做動態腳本的解析、編譯及運算,本文介紹下如何使用能保證線程安全。


引入groovy的相關依賴

<code><dependency>
<groupid>org.codehaus.groovy/<groupid>
<artifactid>groovy/<artifactid>
<version>2.1.6/<version>
/<dependency>
<dependency>
<groupid>org.codehaus.groovy/<groupid>
<artifactid>groovy-jsr223/<artifactid>
<version>2.1.6/<version>
/<dependency>/<code>

因為腳本的編譯會花費比較長的時間,不可能在每次請求到達時在編譯,所以可以在系統啟動的時候先把腳本編譯後放入到本地緩存,這裡可以使用Map,也可以使用一些其他的框架,比如guava、caffeine都是可以的,後者現在已經是springboot2.x系列首選的本地緩存框架了。

我選擇的是使用編譯好的Script放在Map裡。


在使用的時候有一點需要特別說明,在高併發下會涉及到線程安全的問題。

要保證線程安全,這裡有兩種做法。

第一種如下:

<code>String expression = = "if (obj.typeId==1) return '1' else return '111111';";
GroovyShell shell = new GroovyShell();

Script/>Binding binding = new Binding();
JSONObject json = new JSONObject();
json.put("typeId", 1);
binding.setVariable("obj", json);
script.setBinding(binding);
Object evaluate =>

這裡相當於每次請求進來都會new一個新的對象,線程獨享所以線程安全。

第二種方案關鍵代碼如下:

<code>public String equalsCheck() {

Script/>​
Binding binding = new Binding();
binding.setProperty("left", checkContext.getReqJson());
binding.setProperty("right", checkContext.getResJson());

/> return InvokerHelper.createScript(script.getClass(), binding).run().toString();
}/<code>

系統啟動時先進行預編譯,在執行上述方法時先獲得在執行。


這種做法是線程安全的,詳情如下代碼:

springboot安全使用groovyshell


代碼分析可以看出,方法執行時script的初始值為空,這裡的傳參class不為空且為Script的class對象,故會都到else的邏輯裡。

這裡邏輯是根據參數class對象生成一個新的對象,並對變量script進行復制。


如此則每個線程都會有一個單獨的Script對象,獨享的,binding對象也是該Script對象對象的私有變量,也是線程安全的。


分享到:


相關文章: