`
uuhorse
  • 浏览: 64074 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

Java语言的动态性支持(一)

阅读更多
一、脚本语言的支持


    JSR 223中规范了在Java虚拟机上运行的脚本语言与Java程序之间的交互方式。JSR 233是JavaSE6的一部分,在Java表中API中的包是javax.script。目前Java虚拟机支持比较多的脚本语言,比较流行的有JavaScript、Scala、JRuby、Jython和Groovy等。

1. 脚本引擎
    Java中执行脚本需要脚本语言对应的脚本引擎,JSR 223定义了脚本引擎的注册和查找机制。JavaSE6中自带了JavaScript语言的脚本引擎,基于Mozilla的Rhino实现,可以通过三种方式查找脚本引擎:
    ① 通过脚本名称获取:
       
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");

    ② 通过文件扩展名获取:
       
ScriptEngine engine = new ScriptEngineManager().getEngineByExtension("js");

    ③ 通过MIME类型来获取:
       
ScriptEngine engine = new ScriptEngineManager().getEngineByMimeType("text/javascript");


    如下代码,查找注册JavaScript脚本引擎,打印"Hello!",JavaScript脚本中的println是Rhino引擎额外提供的方法。
public class BasicScripting {
    public void greet() throws ScriptException {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        //ScriptEngine engine = manager.getEngineByExtension("js");
        //ScriptEngine engine = manager.getEngineByMimeType("text/javascript");
        if (engine == null) {
            throw new RuntimeException("找不到JavaScript语言执行引擎。");
        }
        engine.eval("println('Hello!');");
    }
    
    public static void main(String[] args) {
        try {
            new BasicScripting().greet();
        } catch (ScriptException ex) {
            Logger.getLogger(BasicScripting.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}


2. 语言绑定
   脚本语言支持API使用语言绑定对象实现Java语言编写的程序与脚本语言间的数据传递。语言绑定对象实际上就是一个简单的哈希表,用来存放和获取需要共享的数据,其定义的接口为javax.script.Bindings,继承自java.util.Map接口。一个脚本引擎在执行过程中可能会使用多个语言绑定对象,不同语言绑定对象的作用域不同。ScriptEngine类提供out和get方法对脚本引擎中特定作用域的默认语言绑定对象进行操作。

使用默认的语言绑定对象:
    public void useDefaultBinding() throws ScriptException {
        ScriptEngine engine = getJavaScriptEngine();
        engine.put("name", "Alex");
        engine.eval("var message = 'Hello, ' + name;");
        engine.eval("println(message);");
        Object obj = engine.get("message");
        System.out.println(obj);
    }


亦可以自定义语言绑定对象(如语言绑定对象中包含程序自己独有的数据等情形……):
    public void useCustomBinding() throws ScriptException {
        ScriptEngine engine = getJavaScriptEngine();
        Bindings bindings = new SimpleBindings();
        bindings.put("hobby", "playing games");
        engine.eval("println('I like ' + hobby);", bindings);
    }


3. 脚本执行的上下文
    脚本引擎通过执行过程中的上下文对象获取与脚本执行相关的信息,同时允许程序员通过此对象配置脚本引擎的行为。其上下文对象来自javax.script.ScriptContext接口,类似于J2EE中javax.servlet.ServletContext接口,该接口主要包含3类信息:

① 输入输出
    默认情况下,脚本输入输出都是在标准控制台中,可以通过setReader和setWriter方法对输出流进行重定向,可以通过setErrorWriter方法进行错误输出重定向。
    //例:将输出重定向到文件
    public void scriptToFile() throws IOException, ScriptException {
        ScriptEngine engine = getJavaScriptEngine();
        ScriptContext context = engine.getContext();
        context.setWriter(new FileWriter("output.txt"));
        engine.eval("println('Hello World!');");
    }


② 自定义属性
    上下文中通过setAttribute和getAttribute方法获取和设置属性,类似于ServletContext中设置和获取属性操作。与ServletContext中不同的是,ScriptContext中的属性是有作用域之分的,ScriptContext按不同的顺序在不同的作用域中进行属性查找(类似于JSP中EL表达式属性的作用域)。通过ScriptContext的getScopes可以得到其中所有可用的作用域,其中预定义了两个作用域:常量ScriptContext.ENGINE_SCOPE(当前的脚本引擎)和ScriptContext.GLOBAL_SCOPE(从同一引擎工厂中创建的所有脚本引擎对象)。
    public void scriptContextAttribute() {
    	ScriptEngine engine = getJavaScriptEngine();
        ScriptContext context = engine.getContext();
        context.setAttribute("name", "Alex", ScriptContext.GLOBAL_SCOPE);
        context.setAttribute("name", "Bob", ScriptContext.ENGINE_SCOPE);
        context.getAttribute("name"); //值为Bob
    }


③ 语言绑定对象
    语言绑定对象位于ScriptContext中,同样也有作用域之分,范围越小,优先级越高。执行如下代码,输出的name值为Bob。
    public void scriptContextBindings() throws ScriptException {
        ScriptEngine engine = getJavaScriptEngine();
        ScriptContext context = engine.getContext();
        Bindings bindings1 = engine.createBindings();
        bindings1.put("name", "Alex");
        context.setBindings(bindings1, ScriptContext.GLOBAL_SCOPE);
        Bindings bindings2 = engine.createBindings();
        bindings2.put("name", "Bob");
        context.setBindings(bindings2, ScriptContext.ENGINE_SCOPE);
        engine.eval("println(name);");    //Bob
    }


    也可以通过ScriptContext获取语言绑定对象:
    public void useScriptContextValues() throws ScriptException {
    	ScriptEngine engine = getJavaScriptEngine();
        ScriptContext context = engine.getContext();
        Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
        bindings.put("name", "Alex");
        engine.eval("println(name);");
    }

    前面说到语言绑定对象存在于上下文环境中,故context中保存的自定义属性其实也是保存于语言绑定对象中的,如2中的语言绑定。
    public void attributeInBindings() throws ScriptException {
    	ScriptEngine engine = getJavaScriptEngine();
    	ScriptContext context = engine.getContext();
    	context.setAttribute("name", "Alex", ScriptContext.GLOBAL_SCOPE);
    	engine.eval("println(name);");
    }


4. 脚本编译
    脚本语言一般均是解释执行的,相对于编译执行的语言,效率较低一些。当脚本语言需要多次重复执行时,可以先对煎熬本进行编译,避免重复解析,提高效率(注:脚本编译需要脚本引擎支持,实现javax.script.Compilable接口)。JavaSE中自带的JavaScript引擎是支持对脚本进行编译的,编译的脚本用javax.script.CompiledScript来表示。
public class ScriptCompile extends JsScriptRunner {
    //对脚本进行编译
    public CompiledScript compile(String scriptText) throws ScriptException {
        ScriptEngine engine = getJavaScriptEngine();
        if (engine instanceof Compilable) {
            CompiledScript script = ((Compilable) engine).compile(scriptText);
            return script;
        }
        return null;
    }
    
    //先编译再执行
    public void run(String scriptText) throws ScriptException {
        CompiledScript script = compile(scriptText);
        if (script == null) {
            return;
        }
        for (int i = 0; i < 100; i++) {
            script.eval();
        }
    }

    public static void main(String[] args) {
        ScriptCompile sc = new ScriptCompile();
        try {
            sc.run("println('Hello');");
        } catch (ScriptException ex) {
            Logger.getLogger(ScriptCompile.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}


5. 方法调用
    Java虚拟机支持脚本的意义在于实现函数式的编程,即脚本中最重要的便是方法。一些脚本引擎允许使用者单独调用脚本中的某个方法,支持此操作的脚本引擎可以通过实现javax.script.Invocable接口,支持顶层方法或者某对象中成员方法的调用。使用方法调用时最好先检查脚本引擎是否实现了Invocable接口,JavaSE中的JavaScript引擎已实现了Invocable接口。

① 在Java中调用脚本中的顶层方法
    public void invokeFunction() throws ScriptException, NoSuchMethodException {
        ScriptEngine engine = getJavaScriptEngine();
        String scriptText = "function greet(name) { println('Hello, ' + name); } ";
        engine.eval(scriptText);
        Invocable invocable = (Invocable) engine;
        invocable.invokeFunction("greet", "Alex");
    }


② 调用脚本中某对象的成员方法
    public void invokeMethod() throws ScriptException, NoSuchMethodException {
        ScriptEngine engine = getJavaScriptEngine();
        String scriptText = "var obj = { getGreeting : function(name) { return 'Hello, ' + name; } }; ";
        engine.eval(scriptText);
        Invocable invocable = (Invocable) engine;
        Object scope = engine.get("obj");
        Object result = invocable.invokeMethod(scope, "getGreeting", "Alex");   //第一个参数为方法所属对象
        System.out.println(result);
    }


③ 指定脚本中的方法为Java接口的实现
    Greet是Java实现的接口,包含一个方法getGreeting,通过Invocable.getInterface()方法指定脚本中的方法为Java接口的实现。
    public void useInterface() throws ScriptException {
        ScriptEngine engine = getJavaScriptEngine();
        String scriptText = "function getGreeting(name) { return 'Hello, ' + name; } ";
        engine.eval(scriptText);
        Invocable invocable = (Invocable) engine;
        Greet greet = invocable.getInterface(Greet.class);
        System.out.println(greet.getGreeting("Alex"));
    }



分享到:
评论

相关推荐

    Java语言的动态性支持(二)

    NULL 博文链接:https://uuhorse.iteye.com/blog/1706466

    JAVA应用程序设计开发(Java语言是SUN公司开发的一种编程语言。)

    Java语言是SUN公司开发的一种编程语言。 1.1 Java语言产生和发展的背景  Java语言源于Oak语言,这是SUN公司在一项消费性电子产品软件发展方案中采用的语言。Oak的设计目标是用以开发可靠、紧凑、易于移植的分布式...

    Java软件技术文档合集

    Java 是一种简单的面象对象的分布式的解释的健壮的安全的结构中立的可移植的性能很优异的多线程的动态的语言。 面向对象可以说是 Java 最重要的特性。Java 语言的设计完全是面向对象的,它不支持类似 C 语言那样的...

    开源免费的博客系统, Java语言开发, 支持数据库.rar

    许可证:Apache-2.0 开发语言:Java、JavaScript、TypeScript 官网:/ Appsmith 是一个用于构建管理面板、内部工具和仪表板的低代码项目。与超过 15 个数据库和任何 API 集成。构建你需要的一切,速度提高 10 倍。...

    PFMS-一套由Java语言写的私人文件管理系统。.zip

    跨平台性(Write Once, Run Anywhere): Java的代码可以在不同的平台上运行,只需编写一次代码,就可以在任何支持Java的设备上执行。这得益于Java虚拟机(JVM),它充当了代码和底层硬件之间的中介。 面向对象: ...

    Java实时性及嵌入式实时Java处理器研究

    Java语言的面向对象、跨平台、语言级并发支持、安全等特性不仅使它在互联网领域得到广泛应用,也引起了嵌入式领域研究人员的高度重视,他们希望能将Java语言改造成嵌入式及实时系统开发的主流语言来提高开发效率及...

    JAVA_API1.6文档(中文)

    java.lang.annotation 为 Java 编程语言注释设施提供库支持。 java.lang.instrument 提供允许 Java 编程语言代理检测运行在 JVM 上的程序的服务。 java.lang.management 提供管理接口,用于监视和管理 Java 虚拟机...

    常见数据结构及算法(Java语言描述).zip

    动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的...

    数据结构与算法分析 Java语言描述 习题解答.zip

    动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的...

    JAVA上百实例源码以及开源项目

     使用Java语言编写的一款用于反映颜色变化的面板,也就是大家熟悉的颜色调色板演示程序。原理是初始化颜色选择按钮,然后为颜色选择按钮增加事件处理事件,最后实例化颜色选择器。 Java二进制IO类与文件复制操作...

    Twitter的雪花算法SnowFlake,使用Java语言实现。.zip

    动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的...

    《数据结构与算法:Java语言描述》源码.zip

    动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的...

    基于java语言的数据结构及算法实现,LeetCode算法示例.zip

    动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的...

Global site tag (gtag.js) - Google Analytics