ScriptEngine

转载自:https://www.jianshu.com/p/99d0f9122c82

想象一下这样的场景:有一个业务,需要设计一个黑盒系统,可以针对不同的输入参数,动态采取不同的策略及措施和输出,比如平台活动等场景。可能大家会想到的是规则,不同的规则场景下执行不同的一套java代码,也许是个好办法,但该办法可能不能简单的应对产品的热部署。

那我们换一个思路,是不是可以把那段实际经常发生变动的java代码分离出来呢,答案当然是肯定的。

方式一:通过字节码的机制,动态加载新的规则执行类class,也就是之前所说的有点麻烦的但可以解决热部署问题的方案。本文不做过多的探讨

方式二:再换个思路,索性连java代码都换掉,采用更加轻量的脚本语言,来执行我们需要的规则相应逻辑(毕竟我们的目标是完成一件事情,而不是限定某种语言或者某种方式的情况下去完成同样的事情,有时候往往太多的约束并不见得是好事)。值得庆幸的是,jdk在1.6之后,引入了ScriptEngine,提供了java环境中对脚本的调用,详细了解可以自行搜索jsr223规范,可惜的是目前只有个js引擎在jdk中有实现。其他的脚本引擎实现具体以spi的方式,由第三方来实现,比如大名鼎鼎的groovy脚本等。

其实脚本引擎做的事情及时方式一的变形,将对应的脚本进行词法语法的解析,生成javaclass最终在jvm里执行真正的调度。

简单说说ScriptEngine吧:

这是官方的顶定义:ScriptEngine is the fundamental interface whose methods must be  fully functional in every implementation of this specification. These methods provide basic scripting functionality.  Applications written to this  simple interface are expected to work with minimal modifications in every implementation.  It includes methods that execute scripts, and ones that set and get values. 

The values are key/value pairs of two types.  

The first type of pairs consists of  those whose keys are reserved and defined in this specification or  by individual  implementations.  The values in the pairs with reserved keys have specified meanings. 

The other type of pairs consists of those that create Java language Bindings, the values are  usually represented in scripts by the corresponding keys or by decorated forms of them.

简单的说,脚本引擎被设计成这样一个功能接口:数据交换和脚本执行

数据交换表现在调度引擎的时候,允许将数据输入/输出引擎,至于引擎内的数据持有具体方式嘛定义里提到了两种:1.普通的键值对;2.Bindings (interface Bindings extends Map<String,Object>)看到这个应该就能明白

引擎执行嘛,表现为eval(),当然有好多重载,无非就是多种脚本内容载入的输入源换一下,是否有额外数据传入等

其他几个重要的类:

ScriptEngineManager 顾名思义一个脚本引擎的管理类,用来创建脚本引擎,大概的方式就是在类加载的时候通过spi的方式,扫描classpath中已经包含实现的所有ScriptEngineFactory,载入后用来负责生成具体的ScriptEngine.具体有个initEngines方法可以关注一下,通过ServiceLoader来实现spi方式的加载。听说jdbc现在也这么玩了,不过还没实际考证过,有兴趣的小伙伴可以探究探究。

ScriptContext 顾名思义一个脚本引擎执行的上下文,接口中设计了两个scope,分别是ENGINE_SCOPE和GLOBAL_SCOPE其中GLOBAL_SCOPE的信息对相应的ScriptEngineFactory可见,除了这两个scope当然在脚本执行的时候还有私有的一个scope,大概有点像局部变量。

NashornScriptEngine jdk8中对ScriptEngine的一个实现,真正具有执行脚本能力的实现类。

Parse 用来解析脚本的类,具体参考parse()方法,有点语法树的味道

……

稍微带几句ScriptEngine用途:

1.首要的必然是执行脚本,两种方式一次性的解释执行以及编译后的反复执行

2.根据js引擎的一些投机取巧,可以判定一些表达式是否合法(当且仅当你没时间或者不想写一个验证类的时候,杀鸡用下牛刀):

比如eval("23 & 4!");  eval("2 | 3 & (5 & 4)")等逻辑表达式的检查,其中有两点要注意的是,jdk默认提供的脚本引擎并不保证线程的安全性,engineFactory.getParameter("THREADING")可以获得描述,具体好像是null,groovy等工厂返回的好像是MULTITHREADED

猜你喜欢

转载自blog.csdn.net/qq2413273056/article/details/84526969