Java SE 6之剧本引擎 让措施如虎添翼
副标题#e#
此刻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中提供了一些接口来界说一个剧本类型,也就是JSR223。通过实现这些接口,Java SE 6可以支持任意的剧本语言(如PHP或Ruby)。
运行第一个剧本措施
在利用Java SE 6运行剧本之前,必需要知道你的Java SE 6支持什么剧本语言。在javax.script包中有许多的类,但这些类中最主要的是ScriptEngineManager。可以通过这个类获得当前Java SE 6所支持的所有剧本。如下面例子将列出所有可以利用的剧本引擎工场。
import javax.script.*;
import java.io.*;
import java.util.*;
import static java.lang.System.*;
public class ListScriptEngines
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
// 获得所有的剧本引擎工场
List<ScriptEngineFactory> factories = manager.getEngineFactories();
// 这是Java SE 5 和Java SE 6的新For语句语法
for (ScriptEngineFactory factory: factories)
{
// 打印剧本信息
out.printf("Name: %s%n" +
"Version: %s%n" +
"Language name: %s%n" +
"Language version: %s%n" +
"Extensions: %s%n" +
"Mime types: %s%n" +
"Names: %s%n",
factory.getEngineName(),
factory.getEngineVersion(),
factory.getLanguageName(),
factory.getLanguageVersion(),
factory.getExtensions(),
factory.getMimeTypes(),
factory.getNames());
// 获得当前的剧本引擎
ScriptEngine engine = factory.getScriptEngine();
}
}
}
上面的例子必需要在Java SE 6中编译。个中import static java.lang.System.*是新的语法,将System中的所有静态成员举办引用,今后就可以直接利用out、in或err了。
通过运行java ListScriptEngines,将显示如下信息
Name: Mozilla Rhino
Version: 1.6 release 2
Language name: ECMAScript
Language version: 1.6
Extensions: [js]
Mime types: [application/javascript, application/ecmascript, text/javascript, text/ecmascript]
Names: [js, rhino, JavaScript, javascript, ECMAScript, ecmascript]
在最下面一行是剧本的别名,也就是利用它们中的任意一个都可以。获得一个详细的剧本引擎有3种要领。
·按照扩展名获得剧本引擎
ScriptEngine engine = manager.getEngineByExtension("js");
getEngineByExtension的参数就是Extensions:[js]中[…]里的部门。
·按照Mime范例获得剧本引擎
ScriptEngine engine = manager.getEngineByMimeType("text/javascript");
getEngineByMimeType的参数可以是Mime types: [application/javascript, application/ecmascript, text/javascript, text/ecmascript]中的任何一个,可以将text/javascript改成text/ecmascript。
·按照名称获得剧本引擎
ScriptEngine engine = manager.getEngineByName("javascript");
getEngineByName后的参数可以是Names: [js, rhino, JavaScript, javascript, ECMAScript, ecmascript]中的任何一个,如可以将javascript改成ecmascript。
#p#副标题#e#
上面已经接头了执行剧本的第一步,就是获得一个可用的剧本引擎。在完成这项事情之 后就可以操作这个剧本引擎执行相应的剧本了。我们可以利用ScriptEngine的eval要领来执行剧本。eval要领被重载的多次,但最常用的是public Object eval(String script)。
下面的例子演示了如何利用eval要领来执行javascript剧本。
import javax.script.*;
import java.io.*;
import static java.lang.System.*;
public class FirstJavaScript
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
// 获得javascript剧本引擎
ScriptEngine engine = manager.getEngineByName("javascript");
try
{
// 开始运行剧本,并返回当前的小时
Double hour = (Double)engine.eval("var date = new Date();" +"date.getHours();");
String msg;
// 将小时转换为问候信息
if (hour < 10)
{
msg = "上午好";
}
else if (hour < 16)
{
msg = "下午好";
}
else if (hour < 20)
{
msg = "晚上好";
}
else
{
msg = "晚安";
}
out.printf("小时 %s: %s%n", hour, msg);
}
catch (ScriptException e)
{
err.println(e);
}
}
}
上面的例子通过获得当前的小时,并将其转化为问候语。上面的措施的输出信息为:
小时9.0:上午好
#p#分页标题#e#
这个例子最值得留意的是执行的2句剧本,最后一句是date.getHours()。并未将这个值赋给一个javascript变量。这时,eval要领就将这样的值返回。这有些雷同C语言的(…)运算符。如(c=a+b, c + d),这个表达式的返回值是a+b+d。
和剧本语言举办交互
上面例子只是运行了一个很是简朴的剧本。这个剧本是孤独的,并未通过Java向这剧本通报任何的值。固然从这个剧本返回了一个值,但这种返回方法是隐式的。
剧本引擎除了这些简朴的成果,还为我们提供了更强大的成果。甚至可以通过Java向剧本语言中通报参数,还可以将剧本语言中的变量的值取出来。这些成果要依靠ScriptEngine中的两个要领put和get。
put 有两个参数,一个是剧本变量名,另一个是变量的值,这个值是Object范例,因此,可以通报任何值。
get 有一个参数,就是剧本变量的名。
下面的代码通过javascript剧本将一个字符串翻转(这个字符串是通过java传给javascript的),然后通过java获得这个被翻转后的字符后,然后输出。
import javax.script.*;
import java.io.*;
import static java.lang.System.*;
public class ReverseString
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
// 成立javascript剧本引擎
ScriptEngine engine = manager.getEngineByName("javascript");
try
{
// 将变量name和变量值abcdefg传给javascript剧本
engine.put("name", "abcdefg");
// 开始执行剧本
engine.eval("var output = '';" +
"for (i = 0; i <= name.length; i++) {" +
" output = name.charAt(i) + output" +
"}");
// 获得output变量的值
String name = (String)engine.get("output");
out.printf("被翻转后的字符串:%s", name);
}
catch (ScriptException e)
{
err.println(e);
}
}
}
以上代码的输出功效为:被翻转后的字符串:gfedcba
让剧本运行得更快
众所周知,表明运行方法是最慢的运行方法。上述的几个例子无一破例地都是以表明方法运行的。由于Java EE 6的剧本引擎可以支持任何实现剧本引擎接口的语言。有许多这样的语言提供了编译成果,也就是说,在运行剧本之前要先将这些剧本举办编译(这里的编译一般将不是生成可执行文件,而只是在内存中编译成更容易运行的方法),然后再执行。假如某段剧本要运行之交多次的话,利用这种方法长短常快的。我们可以利用ScriptEngine的compile要领举办编译。并不是所有剧本引擎都支持编译,只有实现了Compilable接口的剧本引擎才可以利用compile举办编译,不然将抛出一个错误。下面的例子将演示如何利用compile要领编译并运行javascript剧本。
import javax.script.*;
import java.io.*;
import static java.lang.System.*;
public class CompileScript
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
engine.put("counter", 0); // 向javascript通报一个参数
// 判定这个剧本引擎是否支持编译成果
if (engine instanceof Compilable)
{
Compilable compEngine = (Compilable)engine;
try
{
// 举办编译
CompiledScript script = compEngine.compile("function count() { " +
" counter = counter +1; " +
" return counter; " +
"}; count();");
out.printf("Counter: %s%n", script.eval());
out.printf("Counter: %s%n", script.eval());
out.printf("Counter: %s%n", script.eval());
}
catch (ScriptException e)
{
err.println(e);
}
}
else
{
err.println("这个剧本引擎不支持编译!");
}
}
}
上面的代码运行后的显示信息如下:
Counter: 1.0
Counter: 2.0
Counter: 3.0
在这个例子中,先通过compile要领将剧本编译,然后通过eval要领多次举办挪用。在这段代码中只有一个函数,因此,eval就返回了这个函数的值。
动态挪用剧本语言的要领
#p#分页标题#e#
上面的例子只有一个函数,可以通过eval举办挪用并将它的值返回。但假如剧本中有多个函数或想通过用户的输入来抉择挪用哪个函数,这就需要利用invoke要领进动作态挪用。和编译一样,剧本引擎必需实现Invocable接谈锋可以动态挪用剧本语言中的要领。下面的例子将演示如何通过动态挪用的方法来运行上面的翻转字符串的javascript剧本。
import javax.script.*;
import java.io.*;
import static java.lang.System.*;
public class InvocableTest
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
if (engine instanceof Invocable)
{
try
{
engine.eval("function reverse(name) {" +
" var output = '';" +
" for (i = 0; i <= name.length; i++) {" +
" output = name.charAt(i) + output" +
" } return output;}");
Invocable invokeEngine = (Invocable)engine;
Object o = invokeEngine.invoke("reverse", name);
out.printf("翻转后的字符串:%s", name);
}
catch (NoSuchMethodException e)
{
err.println(e);
}
catch (ScriptException e)
{
err.println(e);
}
}
else
{
err.println("这个剧本引擎不支持动态挪用");
}
}
动态实现接口
剧本引擎尚有一个更吸引的成果,那就是动态实现接口。如我们要想让剧本异步地执行,即通过多线程来执行,那InvokeEngine类必需实现Runnable接谈锋可以通过Thread启动多线程。因此,可以通过getInterface要领来使InvokeEngine动态地实现Runnable接口。这样一般可分为3步举办。
1. 利用javascript编写一个run函数
engine.eval("function run() {print('异步执行');}");
2. 通过getInterface要领实现Runnable接口
Runnable runner = invokeEngine.getInterface(Runnable.class);
3. 利用Thread类启动多线程
Thread t = new Thread(runner);
t.start();
下面是实现这个成果的具体代码。
import javax.script.*;
import static java.lang.System.*;
public class InterfaceTest
{
public static void main(String args[])
{
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
try
{
engine.eval("function run() {print('异法式用');}");
Invocable invokeEngine = (Invocable)engine;
Runnable runner = invokeEngine.getInterface(Runnable.class);
Thread t = new Thread(runner);
t.start();
t.join();
}
catch (InterruptedException e)
{
err.println(e);
}
catch (ScriptException e)
{
System.err.println(e);
}
}
}
其实上面的代码是通过javascript实现了Runnable接口的run要领。