首页 > PHP资讯 > Python培训 > ScriptEngine 进阶技巧(JDK 6.0)

ScriptEngine 进阶技巧(JDK 6.0)

Python培训

从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)

本文由欣才IT学院整理发布,未经许可,禁止转载。