`

JAVA的动态性之脚本语言支持API

阅读更多

JAVA语言是一种静态类型的编程语言。静态类型的含义是指在编译的时候进行类型检查。JAVA源代码中的每个每个变量的类型都需要显式地进行声明。所有的变量、方法的参数和返回值的类型在程序运行之前就必须是已知的。JAVA语言的这种静态类型特性使编译器可以在编译的时候执行大量的检查来发现代码中明显的类型错误,这样一来,代码中会包含很多不必要的类型声明,使用代码不够简洁灵活。与静态类型语言相对应的是动态类型语言,如JavaScript和Ruby等。动态类型语言的类型检查在运行中进行。源代码中不需要显式地声明类型。去掉了类型声明之后,使用动态类型语言写的代码更加简洁。近年来,动态类型语言的流行也反映了语言中动态性的重要性。适当的动态性对于提高开发的效率是有帮助的,可以减少开发人员需要编写的代码量。

 

从Java6引入了脚本语言支持API,文本主要围绕脚本语言支持API讲解(反射将在下一文提起)

 

随着JAVA平台的流行,很多的脚本语言(scripting language)都可以运行在JAVA虚拟机上,其中比较流行的有JavaScript、JRuby、Jython和Groovy等。对于这些运行在JAVA虚拟机平台上的脚本语言来说,并不需要为它们准备额外的运行环境,直接复用已有的JAVA虚拟机环境即可。在应用开发中使用脚本语言,实际上是“多语言开发”的一种很好的实践,即根据应用的需求和语言本身的特性来选择最合适的变成语言,以快速高效的解决应用中的问题,比如一个一用,可以用Groovy来编写用户界面,用Java编写核心业务逻辑,用Ruby来进行数据处理,不同的语言编写的代码可以同时运行在同一个JAVA虚拟机上。这些脚本语言于Java语言之间的交互,是由脚本语言支持API来完成的。

1、脚本引擎

来一个简单的例子(相当于java中System.out.println方法),了解一下脚本引擎的用法,代码如下

public void greet() throws ScriptException{
        ScriptEngineManager manager=new ScriptEngineManager();
        ScriptEngine engine=manager.getEngineByName("JavaScript");
        if(engine==null)
            throw new RuntimeException("未找到JavaScript语言执行引擎");
        engine.eval("println('HelloWorld!');");
    }

上面的代码中我们是通过脚本引擎的名称来查找的。实际上,脚本引擎管理器提供了三种查找脚本引擎的方式,分别是通过名称、文件扩展名和MIME类型来完成,如:

ScriptEngine engine=manager.getEngineByExtension("js");
ScriptEngine engine=manager.getEngineByMimeType("text/javascript");
ScriptEngine engine=manager.getEngineByName("JavaScript");

2、语言绑定

所谓的语言绑定对象就是一个简单的哈希表,用来存放和获取需要共享的数据。所有的数据都对应这个哈希表中的一个条目,是简单的键值对。接口javax.script.Bindings定义了语言绑定对象的接口,它继承自java.util.Map接口。一个脚本引擎在执行过程中可能会使用多个语言绑定对象,用来存放在执行过成功产生的全局对象等。ScriptEngine类提供了put和get方法对脚本引擎中特定作用域的默认语言绑定对象进行操作。程序可以直接使用这个默认的语言绑定对象,也可以使用自己的语言绑定对象。在脚本语言的执行过程中,可以将语言绑定对象看成是一个额外的变量映射表。

 

脚本引擎默认的语言绑定对象示例:

首先通过ScriptEngine的put方法向脚本引擎默认的语言绑定对象中添加一个名为name的字符串,接着在脚本中直接根据名称来引用这个对象。同样,在脚本中创建的全局变量message也可以通过ScriptEngine的get方法来获取。这样就实现了Java程序与脚本之间的双向数据传递。数据传递过程中的类型转换是有脚本引擎类完成的,转换规则取决于具体的脚本语言的语法。

public void useDefaultBinding() throws ScriptException{
        ScriptEngine engine=getScriptEngine();
        engine.put("name","andy");
        engine.eval("var message='Hello,'+name;");
        engine.eval("println(message);");
        Object obj=engine.get("message");
        System.out.println(obj);
    }

 

自定义语言绑定对象的示例:

如果希望使用自己的语言绑定对象,可以调用脚本引擎的createBindings方法或者创建一个javax.script.SimpleBindings对象,并传递给脚本引擎的eval方法。通过eval方法传递的语言绑定对象,仅在当前eval调用中生效,并不会改变引擎默认的语言绑定对象

public void useCustomBinding()throws ScriptException{
        ScriptEngine engine=getScriptEngine();
        Bindings bindings=new SimpleBindings();
        bindings.put("hobby","playing gemes");
        engine.eval("println('I like '+hobby);",bindings);
    }

3、脚本执行上下文

与脚本引擎相关的另外一个重要的接口是javax.script.ScriptContext,其中包含脚本引擎执行过程中的相关上下文信息,可以与JavaEE中的servlet规范中的javax.servlet.ServletContext接口进行类型。脚本引擎通过此上下文对象来获取与脚本执行相关的信息,也允许开发人员通过次对象类配置脚本引擎的行为。该上下文对象中主要包括3类信息。

3.1、输入输出

其中包括脚本在执行中用来读取数据输入的java.io.Reader对象以及输出正确内容和错误信息的java.io.Writer对象。在默认情况下,脚本的输入输出都发生在标准控制台中,如果希望把脚本的输出写入到文件中,可以通过setWriter方法把脚本的输出重定向到一个文件中。通过ScriptContext的setReader和setErrorWriter方法可以分别设置脚本执行时的数据出入来源和产生错误时的出错信息的输出目的。代码如下:

public void scriptToFile()throws IOException,ScriptException{
        ScriptEngine engine=getScriptEngine();
        ScriptContext context=engine.getContext();
        context.setWriter(new FileWriter("output.txt"));
        engine.eval("println('Hello World!');");
    }

3.2、自定义属性

ScriptContext中也有与ServletContext中类似的获取和设置属性的方法,即setAttribute和getAttribute。所不同的是ScriptContext中的属性是有作用于之分的。设置属性的时候需要显式地指定所在的作用于。在获取属性的时候,即可以选择指定的作用于中查找,也可以选择根据作用于优先级自动进行查找。脚本上下文实现中包含的作用域是固定的,开发人员不能随意定义自己的作用于。通过ScriptContext的getScopes方法可以得到所有可用的作用于列表。ScriptContext中预先定义了两个作用于:常量ScriptContext.GLOBAL_SCOPE表示的作用于对应的是从一个引擎工厂中创建出来的所有脚本引擎对象,而ScriptContext.ENGINE_SCOPE表示的作用域对应的是当前的脚本引擎。后者优先于前者。

 

作用域影响同名属性查找的示例:

ENGINE_SCOPE中的属性name隐藏了GLOBAL_SCOPE中的同名属性

public void scriptContextAttribute() throws ScriptException {
        ScriptEngine engine = getScriptEngine();
        ScriptContext context = engine.getContext();
        context.setAttribute("name", "shanshouchen", ScriptContext.ENGINE_SCOPE);
        context.setAttribute("name", "andy", ScriptContext.GLOBAL_SCOPE);
        engine.eval("println(name);");//值为shanshouchen
    }

3.3、语言绑定对象

脚本执行上下文中的最后一类信息是语言绑定对象。语言绑定对象也是与作用于相对应的。同样的作用域优先顺序对语言绑定对象也适用。这样的优先级顺序会对脚本执行时的变量解析产生影响。

 

语言绑定对象的优先级顺序的示例:

两个不同的语言绑定对象中都有名称为name的对象,而在脚本的执行过程中,作用域ENGINE_SCOPE的语言绑定对象的优先级较高,因此变量name的值是"andy”

public void scriptContextBindings() throws ScriptException {
        ScriptEngine engine = getScriptEngine();
        ScriptContext context = engine.getContext();
        Bindings b1 = engine.createBindings();
        b1.put("name", "shanshouchen");
        context.setBindings(b1, ScriptContext.GLOBAL_SCOPE);
        Bindings b2 = engine.createBindings();
        b2.put("name", "andy");
        context.setBindings(b2, ScriptContext.ENGINE_SCOPE);
        engine.eval("println(name);");
    }

 

 

通过ScriptContext的setBindings方法设置的语言绑定对象会影响到ScriptEngine在执行脚本时的变量解析。ScriptEngine的put和get方法所操作的实际上就是ScriptContext的作用域为ENGINE_SCOPE的语言绑定对象。如下代码,从ScriptContext中得到语言绑定对象之后,可以直接对这个对象进行操作。如果在ScriptEngine中的eval方法中没有指明的语言绑定对象,实际上起作用的是ScriptContext中作用域为ENGINE_SCOPE的语言绑定对象

通过脚本执行上下文获取语言绑定对象的示例:

public void useScriptContextValues() throws ScriptException {
        ScriptEngine engine = getScriptEngine();
        engine.put("name", "shanshouchen");
        ScriptContext context = engine.getContext();
        Bindings bindings = context.getBindings(ScriptContext.GLOBAL_SCOPE);
        bindings.put("name", "andy");
        engine.eval("println(name);");//输出andy
    }

 

不直接操作语言绑定对象本身,通过ScriptContext的setAttribute类像语言绑定对象中添加数据。所添加的数据在脚本执行时也同样是可见的

自定义属性保存在语言绑定对象中的示例:

public void attributeInBindings()throws ScriptException{
        ScriptEngine engine=getScriptEngine();
        ScriptContext context=engine.getContext();
        context.setAttribute("name","Andy",ScriptContext.GLOBAL_SCOPE);
        engine.eval("println(name);");//输出为Andy
    }

4、方法调用

在脚本中,最常见和最实用的就是方法。有些脚本引擎允许使用者单独调用脚本中的某个方法。支持这种方法调用方式的脚本引擎可以实现javax.script.Invocable接口。通过Invocable接口可以调用脚本中的顶层方法,也可以调用对象中的成员方法。如果脚本中顶层方法或者对象中的成员方法实现了JAVA中的接口,可以通过Invocable接口中的方法来调用脚本中相应的JAVA接口的实现对象。这样可以在JAVA语言中定义接口,在脚本中实现接口。程序中使用该接口的其他部分代码并不知道接口是由脚本来实现的。与Compilable接口一样,ScriptEngine对Invocable接口的实现也是可选的。

 

在JAVA中调用脚本中顶层方法的示例:

通过Invocable接口的invokeFunction来调用脚本中的顶层方法,调用时的参数会被传递给脚本中的方法。因为JAVA SE自带的JavaScript脚本引擎实现了Invocable接口,所以这里省去了对引擎是否实现了Invocable接口的判断

public void invokeFunction() throws ScriptException, NoSuchMethodException {
        ScriptEngine engine = getScriptEngine();
        String scriptText = "function greet(name){println('Hello,'+name);}";
        engine.eval(scriptText);
        Invocable invocable = (Invocable) engine;
        invocable.invokeFunction("greet", "Alex");
    }

 

 

在JAVA中调用脚本中对象的成员方法的示例:

如果被调用的方法是脚本中对象成员方法,就需要使用invokeMethod方法,代码中的getGreeting方法是属性对象obj的,在调用的时候把这个对象作为参数传递进来。

public void invokeMethod() throws ScriptException, NoSuchMethodException {
        ScriptEngine engine = getScriptEngine();
        String scriptText = "var obj={getGreeting:function(name){return 'Hello,'+name;}};";
        engine.eval(scriptText);
        Invocable invocable = (Invocable) engine;
        Object scrpe = engine.get("obj");
        Object result = invocable.invokeMethod(scrpe, "getGreeting", "Alex");
        System.out.println(result);
    }

 

 

在脚本中实现JAVA接口的示例:

在有些脚本引擎中,可以在JAVA语言中定i接口,并在脚本中编写接口的实现。这样程序中的其他部分可以只同JAVA接口交互,并不需要关心接口是由什么方式来实现的。代码清单中,Greet是用JAVA定义的接口,其中包含一个getGreeting方法。在脚本中实现这个接口。通过getInterface方法可以得到由脚本实现的这个接口的对象,并调用其中的方法。

public void useInterface() throws ScriptException{
        ScriptEngine engine=getScriptEngine();
        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"));
    }

Greet.java

public interface Greet {
    String getGreeting(String name);
}

 

 

注:方法invokeMethod与方法invokeFunction的用法差不多,区别在于invokeMethod要指定包含带调用方法的对象。

分享到:
评论

相关推荐

    java 动态脚本语言 精通 Groovy

    Groovy是一种基于Java平台的动态脚本语言,它在Java开发者中越来越受欢迎,因为它提供了简洁、灵活的语法,以及强大的动态编程能力。Groovy与Java兼容性极佳,可以直接调用Java类库,使得它在Java生态系统中具有广泛...

    第 1 部分: 引入 Java 脚本 API

    通过Java脚本API,开发者可以利用脚本语言的灵活性和简洁性,同时保持Java的系统级稳定性和安全性。 【描述】: 虽然描述中没有提供具体信息,但通常介绍一个引入Java脚本API的主题可能涉及如何将脚本引擎集成到...

    编译性语言、解释性语言和脚本语言

    此外,解释性语言通常对错误更宽容,适合快速原型开发和动态脚本编写。 脚本语言,如JavaScript、PHP、VBScript和Perl,它们介于HTML和传统的编程语言之间,具有比解释性语言更简单的语法结构。脚本语言主要用于...

    脚本语言支持组件支持库

    脚本语言支持组件是软件开发中的一个重要组成部分,它主要用于增强应用程序对各种脚本语言的兼容性和集成能力。在本文中,我们将深入探讨脚本语言支持组件的概念、功能、应用场景以及如何利用它来提升软件的灵活性和...

    Java脚本语言程序员手册

    手册详细介绍了Java Scripting API(JSR 223)以及相关的脚本框架,如Rhino和Nashorn,这些都是Java平台中的重要组成部分,用于增强应用程序的动态性与交互性。 一、Java Scripting API (JSR 223) JSR 223是Java的...

    Java与脚本语言的整合与应用.pdf

    本文探讨了Java和脚本语言的整合与应用,讨论了脚本语言的开发特点和Java与脚本语言的整合理由、整合方法和注意事项,并对Java与其他语言的互操作性进行了研究,为程序开发者提供了一个合适的选择来平衡利用Java和...

    Java 1.6 API 中文 New

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

    Java脚本教程(学习资料)

    Java Scripting API使得Java应用能够无缝集成多种脚本语言,利用它们的灵活性和动态特性。这在处理快速原型、测试、配置或者需要动态行为的场景中非常有用。 总之,Java脚本教程涵盖了如何在Java应用程序中集成和...

    Java 8 Nashorn 脚本引擎

    5. **脚本API**:Java 8引入了`javax.script`包,提供了ScriptEngine接口,使得在Java应用中运行脚本变得更加方便。 在实际应用中,Nashorn可以用于以下场景: 1. **自动化测试**:编写JavaScript脚本以自动化Java...

    在Java中运行Perl脚本 JERL

    Java是一种广泛使用的面向对象的编程语言,而Perl则是一种功能强大的脚本语言,常用于文本处理和系统管理任务。在某些场景下,我们可能需要在Java应用程序中调用Perl脚本来执行特定的功能。JERL(Java-Embedding for...

    JAVA 脚本应用数据

    在Java中,你可以利用内置的ScriptEngine接口和相关的脚本引擎实现对不同脚本语言(如JavaScript、Groovy、Ruby等)的支持,从而在应用程序中进行动态计算、数据处理或者扩展功能。JavaMail则是Java中用于处理电子...

    基于java的面向对象的脚本语言 ObjectScript.zip

    - ObjectScript具有脚本语言的动态性,允许在运行时修改代码,无需编译即可执行。 - 它通常具有更简单的语法,如不需要分号结束语句,更少的括号使用,这使得代码更加简洁。 - 动态类型让变量的类型在运行时自动...

    纯Java 实现的一个脚本语言 语法类似javascript

    4. **灵活性**:由于是脚本语言,Scorpio-Java可能支持动态代码修改和运行,方便进行快速原型开发和调试。 5. **性能**:尽管基于Java,但设计时可能考虑了性能优化,确保脚本的执行效率。 6. **可扩展性**:可能...

    javaapi中文版

    其中,动态语言支持让Java平台更好地运行脚本语言,NIO.2(New I/O API)提供了异步I/O操作和文件系统查询等功能,使得I/O处理更加高效。 `JDK_API_1_6_zh_CN.CHM` 和 `JDK_API_1_5_zh_CN.CHM` 是这两个版本的API...

    java6 api中文版

    4. 动态语言支持:Java 6开始支持JSR 223(Scripting for the Java Platform),允许在Java程序中嵌入和执行各种脚本语言,如JavaScript、Groovy等。 5.XML处理:Java 6增强了对XML的支持,包括SAX、DOM和StAX解析...

    JAVA API文档|JAVA API 1.7中文文档

    9. **动态语言支持**:Java 7引入了`invokedynamic`指令,为运行时绑定方法调用提供了支持,这使得Java平台能够更好地支持脚本语言和动态类型语言。 10. **改进的垃圾收集**:G1垃圾收集器在Java 7中成为了一个生产...

    Java脚本言程序手册语员

    Java脚本API设计用于在Java应用程序中嵌入脚本引擎的能力,无需依赖具体的脚本语言。这一API的核心价值在于提供了一种灵活的方式,使得开发者能够根据实际需求选择最适合项目的脚本语言,同时也允许最终用户根据个人...

Global site tag (gtag.js) - Google Analytics