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对象对象的私有变量,也是线程安全的。


分享到:


相關文章: