Rhino脚本引擎技术介绍

摘要:Rhino是一个开源的脚本引擎框架,可以运行类似javascript语法的脚本,并可以调用java的方法,并可以嵌入Java执行,脚本修改后不需要重启JVM进程,就可以使用解析或编译方式执行,非常方便。

 

 

Rhino是一个开源的脚本引擎框架,可以运行类似javascript语法的脚本,并可以调用java的方法,并可以嵌入Java执行,脚本修改后不需要重启JVM进程,就可以使用解析或编译方式执行,非常方便。由于Rhino脚本中可以写入任何表达式和javacript程序,既可以进行条件规则的判断,也可以进行各类简单或复杂的计算,因此是BPS中以前参与者规则和连线规则的一个良好替代方案,在某银行新一代流程平台中,我们使用了Rhino脚本引擎来替代以前基于Antlr词法分析器的规则引擎。

 

 

由于Rhino的灵活和强大的功能,从JDK1.6开始,JDK将Rhino开源软件纳入JDK内部API中,形成了以javax.script为包名的脚本引擎API。

 

 

使用Rhino有如下好处:

1、实现简单,灵活,功能强大,对比以前BPS规则用的Antlr词法分析器,实现更加简单,不需要进行规则文件编辑和代码生成(而且对不同规则需要生成多套代码,很不灵活),脚本引擎可以进行几乎任何运算或Java调用,能满足某银行新一代流程平台的要求。

2、即时生效,修改脚本后不用重启Java进程就可以立即生效运行。

3、有编译和解析两种运行方式,编译方式在大量并发的调用情况下性能更好。

4、轻量,JDK内置,不需要引入其他第三方jar。

 

 

下面主要介绍JDK内置的Rhino脚本引擎,以及其javax.script的API用法。

翻开JDK1.6的javax.script的API,可以看到脚本API中只包含6个接口和6个类(其中一个还是一个异常),整个API非常简单易用。

 

主要的类图如下(类图做了一些省略,对一些不常用的类和接口省略了,并只显示了主要的方法):

 

 

Bindings接口可以理解为上下文,可以往上下文中设置一个Java对象或通过key获取一个对象,它有一个实现类,SimpleBindings,内部就是一个map。

 

上下文是给脚本引擎执行脚本时使用的,脚本引擎在执行脚本的时候,用到上下文中放置的Java对象,执行其方法,使用其属性。

 

ScriptEngine接口就是脚本引擎,用于执行脚本计算结果的接口,其实现类是AbstractScriptEngine和底层的RhinoScriptEngine,这些类是脚本引擎的核心类。eval(String script)和eval(String script, Bindings n)两个方法就是执行一段脚本返回计算结果的两个方法,第二个方法会传入上下文,即运行脚本时,脚本需要使用上下文中设置的Java变量的方法或属性。

 

上下文是有范围的,分三种范围:

 

  •    全局的:所有脚本引擎都可以使用的,由ScriptContext. GLOBAL_SCOPE常量来定义其范围的名称。ScriptEngine.getBindings(ScriptContext.GLOBAL_SCOPE)可以获GLOBAL取这类上下文。
  •    引擎即的:只是一个脚本引擎可以使用的,由ScriptContext. ENGINE_SCOPE常量来定义其范围的名称。ScriptEngine.getBindings(ScriptContext.ENGINE_SCOPE)可以获取这类上下文。
  •    局部的:引擎的一次计算用到的Bindings,没有常量定义,ScriptEngine.createBindings()创建的就是这类上下文。

 

 

ScriptEngineManager可以认为是脚本引擎的一个管理类或工厂,一般我们获取javascript脚本引擎时,会调用这个类实例的getEngineByName("js")方法来获得支持js语法的脚本引擎。

 

脚本引擎在执行脚本的时候有两种方式:

 

  •    解释执行:直接运行ScriptEngine的eval方法
  •    编译执行:运行CompiledScript的eval方法,脚本引擎的内部实现类RhinoScriptEngine又实现了Compilable接口,可以将脚本语句编译成编译后脚本(CompiledScript),CompiledScript可以直接运行eval方法,进行编译方式的脚本执行。

 

 

按理来说,编译执行要快于解释执行,但在实际的测试中发现,只有在大量计算的情况下(循环次数在1-10万以上),编译执行快于解释执行,否则,解释执行可能更快。

 

下面举例说明脚本引擎的基本用法:

 

 

   进行简单的脚本表达式计算:

   package demo.scriptengine;
   import javax.script.ScriptEngine;
   import javax.script.ScriptEngineManager;
   import javax.script.ScriptException;
   public class SimpleScript {
   public static void main(String[] args) throws ScriptException {
   ScriptEngineManager seManager=new ScriptEngineManager();
   ScriptEngine se=seManager.getEngineByName("js");
   Object ret=se.eval("3+4;");
   System.out.println(ret);
   }
   }

计算结果打印出7.0.

 

 

   使用Bindings上下文计算

   package demo.scriptengine;
   import javax.script.Bindings;
   import javax.script.ScriptEngine;
   import javax.script.ScriptEngineManager;
   import javax.script.ScriptException;
   public class ScriptUsingBindings {
   public static void main(String[] args) throws ScriptException {
   ScriptEngineManager seManager=new ScriptEngineManager();
   ScriptEngine se=seManager.getEngineByName("js");
   Bindings bindings=se.createBindings();
   bindings.put("user", new User("张三",19));
   Object ret=se.eval("print(user.getName()); if(user.age>=18) '已成年'; else '未成年';",bindings);
   System.out.println(ret);
   }
   }

User是一个对象,用name和age两个属性和getName()和getAge()两个方法,在脚本中使用user.age和user.getAge()都可以。

 

   计算后结果返回:张三已成年

   以上两个例子都是使用解释方式进行计算,下面的例子使用编译的方式进行计算。

 

   使用编译方式进行计算。

   package demo.scriptengine;
   import javax.script.Bindings;
   import javax.script.Compilable;
   import javax.script.CompiledScript;
   import javax.script.ScriptEngine;
   import javax.script.ScriptEngineManager;
   import javax.script.ScriptException;
   public class CompiledUsingBindings {
   public static void main(String[] args) throws ScriptException {
   ScriptEngineManager seManager=new ScriptEngineManager();
   ScriptEngine se=seManager.getEngineByName("js");
   Compilable ce=(Compilable)se;
   String script="println(user.getName()+'的年龄为'+user.getAge());if(user.age>=18) '已成年'; else '未成年';";
   CompiledScript cs=ce.compile(script);
   Bindings bindings=se.createBindings();
   bindings.put("user", new User("张三",19));
   Object ret=cs.eval(bindings);
   System.out.println(ret);
   }
   }

控制台打印出:

 

   张三的年龄为19

   已成年

   注意:print,println是脚本引擎内置函数,是向控制台打印输出。

 

   解释方式执行javascript函数

  	package demo.scriptengine;
   import javax.script.ScriptEngine;
   import javax.script.ScriptEngineManager;
   import javax.script.ScriptException;
   public class JSFunc {
   public static void main(String[] args) throws ScriptException {
   ScriptEngineManager seManager=new ScriptEngineManager();
   ScriptEngine se=seManager.getEngineByName("js");
   String script="function sum(a,b) { return a+b; }";
   se.eval(script);
   Object ret=se.eval("sum(3,4)");
   System.out.println(ret);
   }
   }

返回结果为7.0

 

   se.eval(script);

   Object ret=se.eval("sum(3,4)");

   这种语法和"function sum(a,b) { return a+b; } sum(3,4);"语法相同。

 

   编译方式执行javascript函数

   package demo.scriptengine;
   import javax.script.Bindings;
   import javax.script.Compilable;
   import javax.script.CompiledScript;
   import javax.script.ScriptEngine;
   import javax.script.ScriptEngineManager;
   import javax.script.ScriptException;
   public class JSFuncCompiled {
   public static void main(String[] args) throws ScriptException {
   ScriptEngineManager seManager=new ScriptEngineManager();
   ScriptEngine se=seManager.getEngineByName("js");
   Compilable ce=(Compilable)se;
   String script="function sum(a,b) { return a+b; } sum(a,b);";
   CompiledScript cs=ce.compile(script);
   Bindings bindings=se.createBindings();
   bindings.put("a", 3);
   bindings.put("b", 4);
   Object ret=cs.eval(bindings);
   System.out.println(ret);
   }
   }

   返回结果为7.0

 

   JS函数调用的高级用法

   package demo.scriptengine;
   import javax.script.*;
   public class InvokeScriptFunction {
   public static void main(String[] args) throws Exception {
   ScriptEngineManager manager = new ScriptEngineManager();
   ScriptEngine engine = manager.getEngineByName("JavaScript");
   // JavaScript code in a String
   String script = "function hello(name) { print('Hello, ' + name); }";
   // evaluate script
   engine.eval(script);
   // javax.script.Invocable is an optional interface.
   // Check whether your script engine implements or not!
   // Note that the JavaScript engine implements Invocable interface.
   Invocable inv = (Invocable) engine;
   // invoke the global function named "hello"
   inv.invokeFunction("hello", "Scripting!!" );
   }
   }

函数使用高级用法2,调用对象的方法。

   package demo.scriptengine;
   import javax.script.*;
   public class InvokeScriptMethod {
   public static void main(String[] args) throws Exception {
   ScriptEngineManager manager = new ScriptEngineManager();
   ScriptEngine engine = manager.getEngineByName("JavaScript");
   // JavaScript code in a String. This code defines a script object 'obj'
   // with one method called 'hello'.
   String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
   // evaluate script
   engine.eval(script);
   // javax.script.Invocable is an optional interface.
   // Check whether your script engine implements or not!
   // Note that the JavaScript engine implements Invocable interface.
   Invocable inv = (Invocable) engine;
   // get script object on which we want to call the method
   Object obj = engine.get("obj");
   // invoke the method named "hello" on the script object "obj"
   inv.invokeMethod(obj, "hello", "Script Method !!" );
   }
   }

调用js文件

   package demo.scriptengine.calljava;
   import java.io.FileReader;
   import javax.script.ScriptEngine;
   import javax.script.ScriptEngineManager;
   public class ListFileTest {
   public static void main(String[] args){
   ScriptEngineManager manager = new ScriptEngineManager();
   ScriptEngine engine = manager.getEngineByName("js");
   String jsFilename="listFiles.js";
   try {
   engine.eval(new FileReader(jsFilename));
   } catch (Exception e) {
   e.printStackTrace();
   }
   }
   }
   listFiles.js内容如下:
   importClass(java.io.File);
   var rootDir = new File("c:/");
   var files = rootDir.listFiles();
   var fixlength=40;
   for(var i=0;i<files.length;i++){
   var apath=files[i].getAbsolutePath();
   print(apath);
   for(var j=0;j<fixlength-apath.length();j++)
   print(" ");
   println((files[i].isDirectory()?"dir":"file"));
   };

控制台输出c盘的目录文件列表:

 

   c:\$360Section dir

   c:\$Recycle.Bin dir

   c:\360SANDBOX dir

   c:\antlr dir

   c:\bar.emf file

   c:\bea dir

   c:\Config.Msi dir

   c:\Documents and Settings dir

   c:\dshell.txt file

   c:\hiberfil.sys file

   c:\HP Universal Print Driver dir

   c:\i-Wifi.lnk file

   c:\kisinstall.dat file

   c:\KRECYCLE dir

   c:\KRSHistory dir

   c:\MSOCache dir

   ... ...

   例子就列举这么多,要学习的同学可以下载JDK1.7的JavaDoc,里面有scripting的学习资料。下面,写脚本要注意下面的事项:

   脚本可以由返回值,也可以没有返回值。如果要返回值,返回语句一定要写在最后一行。如:

   var a=5;

   var b=6;

   a;

   b;

 

   上面语句只会返回b的值6,不会返回a的值5.

   返回值不支持这样的语法:"var a=5;",应该是"var a=5; a;"

   每一个语句后建议加";",如果一行就是一个语句,可以不加分号,但如果一行写几个语句,每个语句必须使用分号分隔,否则脚本引擎无法区分每条语句,会报javascript语法错。

   脚本可调用java对象的方法,如果方法名称为javascript的关键字,则方法无法运行,如下面的语法是运行不了的:

   var f=new java.io.File("c:/out.txt");

   f.delete();

 

   delete是Javascript的关键字,所以f.delete()是无法运行的,会报javascript语法错。

   除非在javascript函数里写return语句,在返回语句中使用return是不支持的,如下面的语句是不支持的。

   var a=3*6/2;

   return a;

 

   正确的写法是:

   var a=3*6/2;

   a;

时间: 2025-01-31 09:38:46

Rhino脚本引擎技术介绍的相关文章

《Cocos2D-X游戏开发技术精解》一第1章 Cocos2D-X引擎的介绍

第1章 Cocos2D-X引擎的介绍 Cocos2D-X游戏开发技术精解如果你梦想着创造充满了价值和理念的世界,那么本书将会介绍一个帮你实现梦想的绝佳途径. 游戏正在改变世界,改变人们的生活.它甚至被赋予了神圣的使命--重塑人类积极的未来.在游戏当中,人们可以感觉到平等.充实和愉悦.游戏让人们的交际更加真实.深入和多元.游戏让娱乐业有更大的发展空间,有更多的经济收益,有更具想象力的挑战.通过本书的学习,读者将会掌握制作游戏的本领.制作游戏的过程,充满了兴奋和喜悦.相信阅读本书的读者中,每一个人都

使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍

一.前言      AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市场快速变化的开发团队,以达到节省开发成本.缩短开发时间,快速适应市场变化的目的.      AgileEAS.NET SOA中间件平台提供了敏捷快速开发软件工程的最佳实践,通过提供大量的基础支撑功能如IOC.ORM.SOA.分布式体系及敏捷并发开发方法所支撑的插件开发体系,以及提供了大量的

联合应用技术介绍之Ajax安全基础

ajax|安全 1.介绍 Ajax由于其良好的交互性,在去年很引人注目.Google Suggest 和 Google Maps [ref 1]就是一些Ajax早期的著名应用.现在,企业正在考虑他们如何也能利用Ajax,web开发者在学习它,安全专家在想如何使它变得安全,黑客们在思考如何入侵.所有能提高服务器吞吐量,能产生更多的动态页面传输,而且能为最终用户提供更加丰富的web应用的技术都必然在这个领域出现. Ajax的下一步计划称为"Web 2.0".这篇文章的目的是介绍一些关于现代

Unity3D游戏引擎技术研讨会将在中国上海召开

(编译/小熙)最近PC.MAC.iPhone.ipad.PS和Xbox360等众多平台的引擎效能成为了业界关心的焦点问题,而Unity3D游戏引擎技术研讨会在中国的召开,无疑吸引了各家厂商的目光. 在上海举行的本届研讨会,由韩国国民大学游戏教育学院和中国东华大学联合举办.大会不仅是对Unity3D游戏引擎技术的介绍,更通过此次会议提升中国先进游戏技术的开发能力,培养后备研发人员. Unity3D游戏引擎技术研讨会最早于今年5月在韩国举行.据悉,现在十种以上的新引擎开发,都是采用了Unity3D游

PHP模板引擎Smarty介绍

模板 用PHP实现MVC开发模式的逻辑层和表示层有多种模板引擎可供选择,但是官方引擎SMARTY诞生后,选择就有了变化.它的理念和实现都是相当"前卫"的.本文主要讨论SMARTY之于其他模板引擎的不同特点,简要介绍了该引擎的安装及使用,并用一个小的测试案例对比了SMARTY和PHPLIB template的速度和易用性. 一.MVC需要模板 MVC最早是在SmallTalk语言的开发过程中总结出的一种设计模式,MVC分别代表了"模型"."视图"和

手把手教你写脚本引擎(三)——简单的高级语言(1,基本原理)

这一篇文章开始讲述如何实现一个高级语言的脚本引擎了.由于工程量较为庞大,因此将分开几篇文章讲.学习做脚本还是要从简单的东西做起的.上一篇文章介绍的命令脚本为实现高级语言的原理做了铺垫.首先,高级语言和低级语言脚本的架构是一致的.其次,为了具有较大的优化的空间,我们将把高级语言转换成低级语言,并配合一个低级语言的脚本引擎来实现高级语言的脚本引擎.当然,习惯上,在这种情况下我们把低级语言叫『指令』. 在这个阶段,我们实现的这门语言是非惰性计算的.弱类型的.仅支持基本类型.数组和函数指针的语言.作为扩

手把手教你写脚本引擎(一)——挑选语言的特性

脚本引擎的作用在于增强程序的可配置性.从游戏到管理系统都需要脚本,甚至连工业级产品的Office.3DS Max以及AutoCAD等都添加了属于自己的脚本语言.DHTML的出现让我们可以在网页代码中嵌入脚本语言,PHP和ASP等技术的出现让我们可以将一个应用程序的界面换成网页,而逻辑使用脚本语言编写.现在脚本语言的种类繁多,Python的发展让BOOST库拥有了对Python的支持,Rails框架的出现壮大了Ruby的实力,LUA更是被大量应用在游戏开发中.Windows甚至提供了wscript

Java SE 6之脚本引擎 让程序如虎添翼

现在Java SE 5已经发布,在明年Java SE 6也将发布.Java SE 6较Java SE5有了很大的改进,它的功能更强,而且是专为Vista所设计,这就意味着Java SE 6将是Vista上的最佳选择.而Java SE 6所提供的最众多的新功能必将成为它的最大卖点. Java SE 6最引人注目的新功能之一就是内嵌了脚本支持.在默认情况下,Java SE 6只支持JavaScript,但这并不以为着Java SE 6只能支持JavaScript.在Java SE 6中提供了一些接口

为Java应用程序加入脚本引擎

前言 现代许多流行的应用程序,越来越多的使用了脚本引擎,最典型的有 Microsoft Office中的VBA等.脚本引擎能提供应用程序极大的可扩展性,也是 被许多热忠于二次开发的使用者所乐意看到的.本文主要讲解 BeanShell----这 样一个Java应用程序脚本引擎,你会了解它的基本特性,及如何将它嵌入到你的 应用程序中.你将看到,为自己的应用程序加上脚本引擎是多么容易的一件事情 . 常见的脚本引擎 现在网络上流行着许多种脚本语言,如TCL,Perl, JavaScript,Python