`

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;

分享到:
评论

相关推荐

    java运行js脚本引擎-----rhino

    Java运行JavaScript脚本引擎——Rhino,是一种在Java平台上执行JavaScript代码的重要工具。Rhino是Mozilla基金会的一个开源项目,它实现了ECMAScript标准,也就是JavaScript的标准化版本。Rhino以其高性能和深度集成...

    基于Rhino引擎的低侵入式JavaScript内置对象扩展方法.pdf

    本文主要介绍了基于Rhino引擎的低侵入式JavaScript内置对象扩展方法。Rhino引擎是一个JavaScript解析引擎,可以解析JavaScript脚本并生成网页信息。为了获取由JavaScript脚本生成的网页信息,更好地获取网页深层信息...

    rhino+javafx实现js脚本调试

    在"rhino+javafx实现js脚本调试"这个主题中,我们将深入探讨如何结合这两个技术来创建一个JS脚本调试环境。首先,Rhino作为一个JavaScript引擎,它的主要职责是解析和执行JavaScript代码。它支持ECMAScript标准,...

    J2ME上的脚本引擎,值得学习!

    在移动设备领域,Java 2 Micro ...总的来说,J2ME上的脚本引擎为开发者提供了更多可能性,尤其是在游戏开发和动态内容生成方面,它降低了开发复杂性,提升了开发效率,是J2ME平台上一个值得深入研究和学习的技术。

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

    随着软件开发技术的不断发展,脚本引擎因其灵活性和可扩展性而在现代应用程序中占据了重要地位。例如,Microsoft Office中的VBA(Visual Basic for Applications)就是一种广泛使用的脚本语言,它极大地增强了Office...

    Java 8 Nashorn 脚本引擎

    Java 8 Nashorn 脚本引擎是Java平台上的一个重要的特性,它为Java应用程序引入了内置的JavaScript引擎,使得开发者能够直接在Java环境中执行JavaScript代码。Nashorn引擎是Oracle公司为Java 8开发的一个高性能、轻量...

    rhino高级技巧

    4. **渲染与材质**:Rhino支持多种渲染引擎,如Flamingo和Penguin,用于创建逼真的图像。教程会教授如何设置光源、材质、相机角度,以及如何使用渲染插件进行后期处理,提升作品的表现力。 5. **插件应用**:Rhino...

    脚本化Java

    `ScriptEngine`是这个包中的核心类,代表一个脚本引擎实例,可以执行脚本并处理脚本的输入输出。`ScriptEngineManager`则用于获取特定类型的脚本引擎,比如通过文件扩展名`.js`来获取JavaScript引擎。`Bindings`接口...

    rhino1_7R1

    Rhino,这个名字源于犀牛,是一种开源的JavaScript引擎,它被设计成在Java平台上运行,让JavaScript能够执行Java代码,从而将JavaScript的功能扩展到了服务器端和更复杂的开发环境中。Rhino1_7R1是Rhino的一个特定...

    基干Rhino引擎的服务器端JavaScript脚木容器设计与实现 (2012年)

    ### 基于Rhino引擎的服务器端JavaScript脚本容器设计与实现 #### 背景与意义 随着Web技术的发展以及JavaScript语言功能的不断增强,JavaScript不再局限于浏览器端的应用,开始逐渐扩展到服务器端领域。这标志着Web...

    Web监控系统中脚本模块的实现.pdf

    编辑器负责提供友好的编程环境,而脚本引擎则负责执行编写的代码,实现预定的控制逻辑。 这种基于Web的脚本编程功能极大地扩展了监控系统的功能性和灵活性,简化了用户操作,提升了系统的实用性。在实际项目中,...

    加载JavaScript脚本的包

    JSR 223是Java的一个规范,定义了脚本引擎的接口,使得不同的脚本语言可以在Java平台上运行。通过这个API,你可以加载并执行各种语言的脚本,包括JavaScript。`ScriptEngineManager`类负责管理所有可用的脚本引擎,...

    Rhino-开源

    - 服务器端脚本:在Java应用服务器中,Rhino可以作为服务器端的脚本引擎,用于动态生成网页内容、处理表单数据等。 - 数据库脚本:在数据库管理系统中,Rhino可以用来编写存储过程,实现更灵活的数据处理逻辑。 - ...

    Rhino Canvas-开源

    Rhino Canvas将这一功能带到了非浏览器环境,特别是那些利用Rhino JavaScript引擎的应用场景,比如Java应用程序、服务器端脚本等。这使得开发者可以在不依赖客户端浏览器的情况下,创建复杂的图形和动画效果,极大地...

    bsf.jar

    BSF 支持多种脚本引擎,包括JavaScript(通过Rhino引擎)、Groovy、Python(Jython)、Perl(JavaPerl)等。这些引擎使得开发者可以在Java环境中编写和运行这些脚本语言的代码。 3. **API接口**:BSF 提供了一套...

    基于Rhino-Python的多圈高簇绒地毯三维仿真.zip

    使用Rhino的内置渲染引擎或者第三方渲染插件进行高质量的图像渲染。 6. **交互式更新**:通过Python,我们可以实现模型的实时预览,以便在设计过程中快速查看和调整地毯的视觉效果。 7. **保存与导出**:最后,将...

    vray3.4 for rhino_5增强渲染器插件.rar

    10. **用户界面和脚本**:V-Ray for Rhino 的用户界面直观且可定制,同时支持自定义脚本和批处理渲染,提高工作效率。 总的来说,V-Ray 3.4 for Rhino 5 是一个强大而全面的渲染工具,它结合了 Rhino 的建模灵活性...

    JSR 223

    这使得开发者能够在不修改核心Java代码的情况下,轻松切换或添加不同的脚本引擎,极大地提高了应用的可维护性和适应性。 #### 实现与应用 **1. **脚本引擎实现**:** 多个脚本语言实现了JSR 223规范,包括但不...

Global site tag (gtag.js) - Google Analytics