当前位置: 首页 > 科技观察

NashornScriptingEngineTutorialforJava8

时间:2023-03-13 17:45:02 科技观察

本文提供通俗易懂的代码示例,让您全面了解NashornJavaScript引擎。NashornJavaScript引擎是JavaSE8的一部分,它与其他独立引擎竞争,例如GoogleV8(它是GoogleChrome和Node.js的引擎)。Nashorn扩展了Java在JVM上运行动态JavaScript脚本的能力。在接下来的15分钟左右,您将学习如何在JVM上动态运行JavaScript。通过一些简短的代码示例演示最近的Nashorn语言功能。了解如何在Java和JavaScript之间相互调用。***包括如何在日常Java业务中集成动态脚本。使用NashornNashornjavascript引擎可以在java程序中以编程方式使用,也可以在$JAVA_HOME/bin目录中的命令行工具jjs中使用。如果要创建jjs符号链接,如下:$cd/usr/bin$ln-s$JAVA_HOME/bin/jjsjjs$jjsjjs>print('HelloWorld');本教程重点介绍在java代码中使用nashorn,所以我们暂时跳过jjs。一个简单的HelloWorldjava代码示例如下:ScriptEngineengine=newScriptEngineManager().getEngineByName("nashorn");engine.eval("print('HelloWorld!');");为了在java代码中执行JavaScript,首先使用原始Rhino中的javax.script包(旧版本Java中来自Mozilla的引擎)创建一个nashorn脚本引擎。.JavaScript代码可以像上面一样直接作为字符串执行,也可以放到js脚本文件中,如:ScriptEngineengine=newScriptEngineManager().getEngineByName("nashorn");engine.eval(newFileReader("script.js"));Nashornjavascript基于ECMAScript5.1,但Nashorn后续版本将支持ECMAScript6:目前的Nashorn策略是遵循ECMAScript规范。当我们发布JDK8时,我们将实现ECMAScript5.1标准。Nashorn的后续版本将实现ECMAScriptEdition6标准。Nashorn定义了许多扩展ECMAScript标准的语言和API。接下来我们看看java与JavaScript的通信。Java调用Javascript函数Nashorn支持java代码直接调用脚本文件中定义的JavaScript函数。您可以使用java对象作为函数的参数,并在调用该函数的java方法中接收返回的数据。下面的JavaScript代码将在java端调用:对象.prototype.toString.call(object));};为了调用函数,您首先必须将脚本引擎转换为Invocable。NashornScriptEngine实现了Invocable接口,定义了一个方法invokeFunction来调用JavaScript函数,传入函数名即可。ScriptEngineengine=newScriptEngineManager().getEngineByName("nashorn");engine.eval(newFileReader("script.js"));Invocableinvocable=(Invocable)engine;Objectresult=invocable.invokeFunction("fun1","PeterParker");系统.out.println(result);System.out.println(result.getClass());//HitherefromJavascript,PeterParker//greetingsfromjavascript//classjava.lang.String上面代码的执行会在上面打印三行信息安慰。调用print函数将输出通过管道传输到System.out控制台,因此我们首先看到的是JavaScript打印的信息。现在我们通过传递一个任意的Java对象来调用第二个函数://[objectjava.time.LocalDateTime]invocable.invokeFunction("fun2",newPerson());//[objectcom.winterbe.java8.Person]您可以传递任何Java对象而不会丢失JavaScript端信息的类型。因为脚本本身是在JVM虚拟机中执行的,所以我们可以充分利用nashorn引擎的JavaAPI和外部库的强大功能。从JavaScript端调用Java方法从JavaScript调用Java方法很简单。首先,我们定义一个静态Java方法:staticStringfun1(Stringname){System.out.format("HitherefromJava,%s",name);return"greetingsfromjava";}JavaScript可以通过Java.typeAPI引用Java类。这类似于在Java类中引入其他类。定义好Java类型后,我们可以直接调用它的静态方法fun1()并将结果打印到sout。因为方法是静态的,所以我们不需要创建类的实例。varMyJavaClass=Java.type('my.package.MyJavaClass');varresult=MyJavaClass.fun1('JohnDoe');print(result);//HitherefromJava,JohnDoe//greetingsfromjava调用java方法时,Nashorn如何处理原生JavaScript类型和java类型转换?让我们通过一个简单的例子来了解一下。以下java方法简单地打印实际类方法参数的类型:staticvoidfun2(Objectobject){System.out.println(object.getClass());}为了理解引擎如何处理类型转换,我使用不同的JavaScript类型来调用java方法:MyJavaClass.fun2(123);//classjava.lang.IntegerMyJavaClass.fun2(49.99);//classjava.lang.DoubleMyJavaClass.fun2(true);//classjava.lang.BooleanMyJavaClass.fun2("hithere")//classjava.lang.StringMyJavaClass.fun2(newNumber(23));//classjdk.nashorn.internal.objects.NativeNumberMyJavaClass.fun2(newDate());//classjdk.nashorn.internal.objects.NativeDateMyJavaClass.fun2(newRegExp());//classjdk.nashorn.internal.objects.NativeRegExpMyJavaClass.fun2({foo:'bar'});//classjdk.nashorn.internal.scripts.JO4原始javascript类型转换为适当的java包装类.而不是适配器类中的本机javascript对象。请记住,这些类来自jdk.nashorn.internal,因此您不应该在客户端使用这些类:任何标记为internal的东西都可能会从您下面更改为ScriptObjectMirror当使用ScriptObjectMirror传入原生JavaScript对象时,实际上是有是代表JavaScript对象的java对象。ScriptObjectMirror实现接口与jdk.nashorn.api内部的映射。此包下的类旨在供客户端代码使用。下一个示例将参数类型Object更改为ScriptObjectMirror,因此我们可以获得有关在JavaScript中传递的对象的一些信息:mirror.getOwnKeys(true)));}当我们将传递的对象散列到方法中时,我们可以在Java端访问这些属性:MyJavaClass.fun3({foo:'bar',bar:'foo'});//Object:[foo,bar]我们也可以在Java端调用JavaScript对象中的函数。我们首先定义一个JavaScript类型Person,它包含属性firstName、lastName和函数getFullName。functionPerson(firstName,lastName){this.firstName=firstName;this.lastName=lastName;this.getFullName=function(){returnthis.firstName+""+this.lastName;}}javascript函数getFullName可以通过callMember()调用脚本对象镜像。staticvoidfun4(ScriptObjectMirrorperson){System.out.println("FullNameis:"+person.callMember("getFullName"));}当我们将一个新的人传递给java方法时,我们可以在控制台中看到预期的结果:varperson1=newPerson("Peter","Parker");MyJavaClass.fun4(person1);//全名是:PeterParker语言扩展Nashorn定义了一系列扩展ECMAScript标准的语言和API。让我们直接了解最酷的功能:类型化数组原始javascript数组是未类型化的。Nashorn允许您在JavaScript中使用java数组:varIntArray=Java.type("int[]");vararray=newIntArray(5);array[0]=5;array[1]=4;array[2]=3;array[3]=2;array[4]=1;try{array[5]=23;}catch(e){print(e.message);//Arrayindexoutofrange:5}array[0]="17";print(array[0]);//17array[0]="wrongtype";print(array[0]);//0array[0]="17.3";print(array[0]);//17int[]数组的行为类似于真正的javaint数组。但是当我们尝试向数组添加非整数值时,Nashorn会执行隐式类型转换。字符串会自动转换为整数,非常方便。集合和ForEach我们可以使用java集合来代替数组。首先使用Java.type定义一个java类型,然后根据需要创建一个实例。varArrayList=Java.type('java.util.ArrayList');varlist=newArrayList();list.add('a');list.add('b');list.add('c');foreach(varelinlist)print(el);//a,b,c为了遍历集合和数组中的元素,Nashorn引入了foreach语句。这就像Java的for循环。下面是一个遍历集合元素的例子:varmap=newjava.util.HashMap();map.put('foo','val1');map.put('bar','val2');foreach(vareinmap.keySet())print(e);//foo,barforeach(vareinmap.values())print(e);//val1,val2Lambda表达式和Streams看来大家更喜欢Lambda和Streams——Nashorn呢!虽然ECMAScript5.1缺少Java8Lambda表达式中的压缩箭头语法,但我们可以在接受Lambda表达式的地方使用函数代替。varlist2=newjava.util.ArrayList();list2.add("ddd2");list2.add("aaa2");list2.add("bbb1");list2.add("aaa1");list2.add("bbb3");list2.add("ccc");list2.add("bbb2");list2.add("ddd1");list2.stream().filter(函数(el){returnel.startsWith("aaa");}).sorted().forEach(function(el){print(el);});//aaa1,aaa2扩展类Java的类型可以简单的通过Java.extend进行扩展,在下一个例子中您将在脚本中创建一个多线程示例:varRunnable=Java.type('java.lang.Runnable');varPrinter=Java.extend(Runnable,{run:function(){print('printedfromseparatethread');}});varThread=Java.type('java.lang.Thread');newThread(newPrinter()).start();newThread(function(){print('printedfromanotherthread');}).start();//printfromseparatethread//printedfromanotherthread参数重载的方法和函数可以使用点符号或方括号调用。varSystem=Java.type('java.lang.System');System.out.println(10);//10System.out["println"](11.0);//11.0System.out["println(double)"](12);//12.0在使用重载参数调用方法时可以传递可选参数来确定调用哪个方法,例如println(double)。对于JavaBean,我们不需要使用getter或setter来访问类成员属性。我们可以直接使用属性名来简单地访问JavaBean中的属性。例如:varDate=Java.type('java.util.Date');vardate=newDate();date.year+=1900;print(date.year);//如果2014函数语法只是简单的一个-line函数,我们可以使用大括号:functionsqr(x)x*x;print(sqr(3));//9属性绑定来自不同对象的属性可以绑定在一起:functionsqr(x)x*x;print(sqr(3));//9字符串处理我喜欢字符串修剪。print("hehe".trimLeft());//heheprint("hehe".trimRight()+"he");//hehehe万一你忘了Whereareyou:print(__FILE__,__LINE__,__DIR__);导入范围这有时在一次导入多个java包时很有用。我们可以使用JavaImporter与with结合使用,在with块中引用:);///path/to/my/script.js}可以直接使用数组转换,不需要使用Java.type或者JavaImporter导入一些包,比如java.util:varlist=newjava.util.ArrayList();list.add("s1");list.add("s2");list.add("s3");下面的代码演示了将java列表转换为JavaScript数组:varjsArray=Java.from(list);print(jsArray);//s1,s2,s3print(Object.prototype.toString.call(jsArray));//[objectArray]其他方式:varjavaArray=Java.to([3,5,7,11],"int[]");调用超类函数在JavaScript中访问重载成员有点笨拙,因为ECMAScript没有与Java的super关键字等价的东西。幸运的是Nashorn有一个解决方案。首先我们在Java代码中定义一个超类:classSuperRunnerimplementsRunnable{@Overridepublicvoidrun(){System.out.println("superrun");}}接下来我们在JavaScript中重载SuperRunner。创建新的Runner实例时请注意Nashorn的扩展语法:其重载成员的语法是基于Java的匿名对象方式。varSuperRunner=Java.type('com.winterbe.java8.SuperRunner');varRunner=Java.extend(SuperRunner);varrunner=newRunner(){run:function(){Java.super(runner).run();打印('onmyrun');}}runner.run();//superrun//onmyrun我们使用Java.super来调用重载方法SuperRunner.run()。在JavaScript中执行其他脚本非常容易。我们可以使用load函数加载本地或远程脚本。我在我的许多Web前端中使用Underscore.js,因此在Nashorn中我们可以重用Underscore:load('http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js');varodds=_.filter([1,2,3,4,5,6],function(num){returnnum%2==1;});print(odds);//1,3,5扩展脚本的执行是在同一个JavaScript上下文中,所以我们可以直接访问下划线变量。请记住,由于变量名称重叠,脚本加载可能会导致代码问题。我们可以通过将加载的脚本文件放入一个新的全局上下文来解决这个问题:loadWithNewGlobal('script.js');命令行脚本如果您对用Java编写命令行脚本感兴趣,请尝试Nake。Nake是用于Java8Nashorn的简单Make实用程序。您可以在Nakefile中定义任务,然后使用naked—myTask运行任务。任务用JavaScript编写并通过Nashorn脚本模式运行,因此您可以在最终应用程序中充分利用Java8API和其他Java库的强大功能。对于Java开发人员来说,编写命令行脚本从未如此简单。总结希望本文对您有所帮助,让您轻松了解NashornJavaScript引擎。在此处、此处和此处阅读有关Nashorn的更多信息。如果想使用Nashorn编写shell脚本,可以参考这里。我之前也发表过一些关于如何在Nashron引擎中使用Backbone.js模型数据的文章。如果你想了解更多关于Java8的知识,可以查看我的文章Java8Tutorial和Java8StreamTutorial。本文中的示例代码可通过GitHub获得,您可以分叉此存储库并通过Twitter向我发送反馈。坚持编程!

猜你喜欢