从JDK 6.0开始支持Script脚本,其介绍可以访问http://www.ibm.com/developerworks/cn/java/j-lo-jse66/。顺便提提这个网站,IBM的DeveloperWorks,链接是中文版本的,内容不如E文的多,但也很丰富,关键是内容很正式都是牛人写的,很可靠。这个网站上也有关于ScriptEngine的文章,也有中文的。
接下来要介绍的是进阶一些的使用,不算是很高级,都是工作中的经验,也让不知道的人少些弯路。
开篇还是要稍微介绍一下,JDK 6.0在中整合的JavaScript解析器是Mozilla的Rhino,Rhino是Mozilla的Java版的Javascript开源解析器,支持JDK1.4、1.5,可以在http://www.mozilla.org/rhino/下载。JDK6.0将其加入,但类和调用方法发生了变化,更加简单好用(符合JSR 223规范),Rhino直接使用有很多不方便的地方,差点让我的项目无法完成。但JDK中的Rhino没有E4X的组件(Script解析xml)。虽然没有E4X,但还是有JDK版的还是有办法解析xml。Java版本的script的解析器还有BeanShell,但似乎很久没有更新。而且据测评速度比Rhino慢,Rhino是最快的。Java开发组选择Rhino还是有道理的。
一个小例子作为引申还是要写的
写一个Javascript function,做a+b运算,在java中调用并得到返回值
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class RunScript {
public static void main(String [] args){
ScriptEngineManager manager = new ScriptEngineManager();
//注意,一定要写javascript写错了就报错
ScriptEngine engine = manager.getEngineByName("javascript");
//Script
String scriptString = "function add(op1,op2){return a+b} add(a,b);";
engine.put("a", 1);
engine.put("b", 2);
try {
//返回的是 最后一次有返回值的一行 所返回的值
//也就是add(a,b)调用后的返回值
Object result = engine.eval(scriptString);
//返回的是3.0 (Double类型)
System.out.println(result);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
这是一个不能在简单的例子了,其中的如果想做表达式运算用这个最简单了,直接写”1+3*4/2″,eval一下直接有结果。
例子中scriptString是Javascript本身,开始有一个function add(op1,op2),然后调用add(a,b),很简单的script。engine.put方法给a和b赋值,eval运行该script。今天要将的进阶技巧,初级的就不说了。现在的需求是,script的编译是最耗时的操作(非常耗时),理想的办法是将script字符串编译成一个Java对象,以后每次调用Java对象速度就有保证。办法当然有,修改。
public static void main(String [] args){
ScriptEngineManager manager = new ScriptEngineManager();
//注意,一定要写javascript写错了就报错
ScriptEngine engine = manager.getEngineByName("javascript");
Compilable compilable = (Compilable) engine;
//Script
String scriptString = "function add(a,b){return a+b} add(a,b);";
CompiledScript script = null;
try {
script = compilable.compile(scriptString);
} catch (ScriptException e1) {
//如果script语法有问题会抛出异常
}
}
将ScriptEngine对象强转成CompiledScript对象,就有了编译Script的方法 CompiledScript script就是Javascript的Java对象实例,例子中没有写如果调用,因为接下来要介绍ScriptEngine中的环境范围。
写function的目的就是写一次后面调用很多次,我们希望这里的script写一次后可以不断的调用,如已经有add function,所以我不必再写一次function add,而是只写String s = “add(a,b)”
这个功能ScriptEngine是支持的,但也要区分,如果有两个线程,script都是各自的,如果恰巧用的function名字是一样的,如果没有范围的概念后运行的function会覆盖先运行的function。
到达这个目标就需要Bindings,一个实现Map接口的类。它会以K-V的形式保存Script中变量名称和值。这样用它运行同一个 Script,由于值不同,结果也不同。而且一个Bindings对象会记住运行过的Function。比如,运行过function add(a,b){return a+b}的script,虽然没有任何计算,只是function的声明,但接下来就可以在另一个地方运行add(a,b);而不需要再声明一次 function.Bindings包括三个个范围,一个是GLOBAL范围,一个是ENGINE范围,一个是最小的实例范围
Bindings globalBindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
Bindings engineBindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
Bindings bindings = engine.createBindings();
三个个范围,一个比一个小,相互equals都是false,而且createBindings得到的bindings每次都新的对象,相互 equals都是false,而getBindings获得的对象,只要engine是同一个对象,返回的
bindings用equals都是true回到前面的对象化script,运行script对象
public class RunScript {
public static void main(String [] args){
ScriptEngineManager manager = new ScriptEngineManager();
//注意,一定要写javascript写错了就报错
ScriptEngine engine = manager.getEngineByName("javascript");
Compilable compilable = (Compilable) engine;
Bindings bindings = engine.createBindings();
String functionScript = "function add(op1,op2){return op1+op2}";
String s= "add(a,b)";
CompiledScript fScript = null;
CompiledScript script = null;
try {
fScript = compilable.compile(functionScript);
script = compilable.compile(s);
} catch (ScriptException e1) {
//如果script语法有问题会抛出异常
}
bindings.put("a", 1);
bindings.put("b", 2);
try {
fScript.eval(bindings);
Object result = script.eval(bindings);
System.out.println(result); //3.0
} catch (ScriptException e) {
//运行时异常
}
}
}
这样编译一次,一直运行的目的就达到了。
现在又有新的要求了:每次createBindings的时候,bindings里面什么都没有,现在希望每次create的时候里面已经有一堆已经定义好的function,如长度判断,日期判断等等。直接在后面的代码中直接调用这些function。
方法就是让GLOBAL_SCOPE的bindings运行一次这些定义好的function,以后create出来的bindings就包含有这些function了。但注意顺序,先运行,后create.这样每个create生成的Bindings也可以有add function了
public class RunScript {
public static void main(String [] args) throws ScriptException{
ScriptEngineManager manager = new ScriptEngineManager();
//注意,一定要写javascript写错了就报错
ScriptEngine engine = manager.getEngineByName("javascript");
Compilable compilable = (Compilable) engine;
Bindings globalBindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
String functionScript = "function add(op1,op2){return op1+op2}";
CompiledScript fScript = compilable.compile(functionScript);
fScript.eval(globalBindings);
//这样每个create生成的Bindings也可以有add function了
Bindings bindings = engine.createBindings();
CompiledScript script = compilable.compile("add(a,b)");
bindings.put("a", 1);
bindings.put("b", 2);
Object result = script.eval(bindings);
System.out.println(result); //3.0
}
}
这样就好像ScriptEngine中有默认有自己定义的function一样,可以无限扩展,而之后的运行速度几乎没有影响,当然第一次加载还是要时间的,但只会加载一次。
这次介绍了预定义的function,下次写预定义的变量,更强。
(常州java培训机构 http://www.thinksite.cn/list-114-1.html)