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

       随着Java平台的流行,很多的脚本语言(scripting language)都可以运行在Java虚拟机啊上,其中比较流行的有JavaScript、JRuby、Jython和Groovy等。相对Java语言来说,脚本语言由于其灵活性非常强,非常适合在某些情况下使用,比如描述应用中复杂多变的业务逻辑,并在应用运行过程中进行动态修改;为应用提供一种领域特定语言(Domainspecific Language,DSL),供没有技术背景的普通用户使用;作为应用中各个组件之间的“胶水”,快速进行组件之间的整合;快速开发出应用的原型系统,从而迅速获取用户反馈,并进行改进;帮助开发人员快速编写测试用例等。等于这些场景,如果使用java来开发则事倍功半。

       对于这些运行在Java虚拟机平台上的脚本语言来说,并不需要为她们准备额外的运行环境,直接复用已有的Java虚拟机环境即可。这就节省了在运行环境上所需的成本投入。在应用开发中使用脚本语言,实际上是“多语言开发”的一种很好的实践,即根据应用的需要和语言本身的特性来选择最合适的变成语言,以快速高效地解决应用中的某一部分问题。多种不同语言实现的组件组合起来,用Java编写核心业务逻辑,用Ruby来进行数据处理。不同语言编写的代码可以同时运行的同一个Java虚拟机之上。这些脚本语言和Java语言之间的交互,是由脚本语言支持API来完成的。

1.脚本引擎

       一段脚本的执行需要由该脚本语言对应的脚本引擎来完成。一个Java程序可以选择同时包含多种脚本语言的执行引擎,这完全由程序的需求来决定。程序中所用到的脚本语言,都需要有相应的脚本引擎。JSR 233中定义了脚本引擎的注册和查找机制。这对于脚本引擎的实现者来说,是需要了解的。而一般的开发人员只需要了解如何通过脚本引擎管理器来获取对应语言的脚本引擎,并不需要了解脚本引擎的注册机制。Java SE6中自带了JavaScript语言的脚本引擎,是基于Mozilla的Rhino来实现的。对于其他的脚本语言,则需要下载对应的脚本引擎的库并放在程序的类路径中。一般只要放在类路径中中,脚本引擎就可以被应用程序发现并使用。

        首先介绍脚本引擎的一般用法。首先创建一个脚本引擎管理器javax.script.ScriptEngineManager对象,再通过管理器来查询所需的JavaScript脚本引擎,最后通过脚本引擎来执行JavaScript代码。

ScriptEngineManagerDemo

执行结果

       上面的代码中是通过脚本引擎的名字进行查找的。实际上,脚本引擎管理共支持三种查找脚本引擎的方式,分别通过名称、文件扩展名和MIME类型来完成。

2.语言绑定

       脚本语言支持API的一个很大优势在于它规范了Java语言与脚本语言之间的交互方式,使Java语言编写的程序可以与脚本之间进行双向的方法调用和数据传递。方法调用的方式会在稍后介绍。数据传递是通过语言绑定对象来完成的。所谓的绑定对象就是一个简单的哈希表,用来存放和获取需要共享的数据。所有数据都对应这个哈希表中的一个条目,是简单的名值对。接口javax.script.Bindings定义了语言绑定对象的接口,继承自java.util.Map接口。一个脚本引擎在执行过程中可能会使用多个语言绑定对象。不同语言绑定对象的作用域不同。在默认情况下,脚本引擎会提供多个语言绑定对象,用来存放在执行过程中产生全局对象等。ScriptEngine类提供了put和get方法对脚本引擎中特定作用域的默认语言绑定对象进行操作。程序可以直接使用这个默认的语言绑定对象,也可以使用自己的语言绑定对象。在脚本语言的执行过程中,可以将语言绑定对象看成是一个额外的变量映射表。在解析变量值的时候,语言绑定对象中的名称也会被考虑在内。脚本执行过程中产生的全局变量等内容,会出现在语言绑定对象中。通过这种方式就完成了Java与脚本语言之间的双向数据传递。

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

    /**
* 脚本引擎默认的语言绑定对象的示例
*/
public static void useDefaultBinding(){
try{
ScriptEngine engine = getJavaScriptEngine();
engine.put("name","Arthur");
engine.eval("var message = 'Hello,' + name;");
engine.eval("print(message);");
Object obj = engine.get("message");
System.out.println(obj);
}catch (Exception e){
System.out.println("异常信息:"+e.getMessage());
}
}

脚本引擎默认的语言绑定对象的示例执行结果

    在大多数情况下,使用ScriptEngine的put和get方法就足够了。如果仅使用put和get方法,语言绑定对象本身对于开发人员来说是透明的。在某些情况下,需要使用程序自己的语言绑定对象,比如语言绑定对象中包含了程序自己独有的数据。如果希望使用自己的语言绑定对象,可以调用脚本引擎的createBindings方法或创建,并传递给脚本引擎的eval方法。

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

    /**
* 自定义语言绑定对象的示例
*/
public static void userCustomBinding(){
try{
ScriptEngine engine = getJavaScriptEngine();
Bindings bindings = new SimpleBindings();
bindings.put("hobby","playe games");
engine.eval("print('I like ' + hobby);",bindings);
}catch(Exception e){
System.out.println("异常信息:"+e.getMessage());
}
}

自定义语言绑定对象的示例的执行结果

    通过eval方法传递的语绑定对象,仅在当前eval调用中生效,并不会改变引擎默认的语言绑定对象。

3.脚本执行上下文

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

3.1输入与输出

    首先介绍与脚本输入和输出的配置信息,其中包括脚本在执行中用来读取数据输入的java.io.Reader对象以及输出正确内容和出错信息的java.io.Writer对象。在默认情况下,脚本的输入输出都在发生在标准控制台中。

把脚本运行时的输出写入到文件中的示例

    /**
* 把脚本运行时的输出写入到文件中。
*/
public static void scriptToFile(){
try {
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
context.setWriter(new FileWriter("output.txt"));
engine.eval("print('Hello World!');");
} catch (ScriptException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

执行结果

说明

    通过setWriter方法把脚本的输出重定向到一个文件中。通过ScriptContext的setReader和setErrorWriter方法可以分别设置脚本执行时的数据输入来源和产生错误时出错信息的输出目的。

3.2自定义属性

    ScriptContext中也有与ServletConext中类似的获取和设置属性的方法,即setAttribute和getAttribute。所不同的是,ScriptContext中的属性是有作用域之分的。不同作用域的区别在于查找属性时的顺序不同。每个作用域都以一个对应的整数表示其查找顺序。该整数值越小,说明查找时的顺序越优先。优先级高的作用域中的属性会隐藏优先级低的作用域中的同名属性。因此,设置属性时需要显式地指定所在地作用域。在获取属性地时候,既可以选择在指定地作用域中查找,也可以选择根据作用域优先级自动进行查找。

    不过脚本执行上下文实现中包含地作用域是固定的,开发人员不能随意定义自己的作用域。通过ScriptContext的getScopes方法可以得到所有可用的作用域列表。ScriptContext中预先定义了两个作用域:

  • 常量ScriptContext.ENGINE_SCOPE表示的作用域对应的是当前的脚本引擎。
  • 常量ScriptContext.GLOBAL_SCOPE表示的作用域对应的是从同一引擎工厂中创建出来的所有脚本引擎对象。

    说明: 前者的优先级较高

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

   /**
* 作用域影响同名属性查找的示例
*/
public static void scriptContextAttribute(){
try{
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
context.setAttribute("name","Arthur Ming",ScriptContext.GLOBAL_SCOPE);
context.setAttribute("name","明国宾",ScriptContext.ENGINE_SCOPE);
System.out.println(context.getAttribute("name"));
System.out.println(context.getAttribute("name",ScriptContext.GLOBAL_SCOPE));
} catch(Exception e){
System.out.println("异常信息:"+e.getMessage());
}
}

执行结果

3.1语言绑定对象

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

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

    /**
* 语言绑定对象的优先级顺序的示例
*/
public static void scriptContextBindings(){
try {
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
Bindings binding1 = engine.createBindings();
binding1.put("name","Arthur Ming");
context.setBindings(binding1,ScriptContext.GLOBAL_SCOPE);
Bindings binding2 = engine.createBindings();
binding2.put("name","明国宾");
context.setBindings(binding2,ScriptContext.ENGINE_SCOPE);
engine.eval("print(name)");
} catch (Exception e){
e.printStackTrace();
}
}

执行结果

    通过ScriptContext的setBindings方法设置的语言绑定对象会影响到ScriptEngine在执行脚本时变量解析。ScriptEngine的put和get方法所操作的实际上就是ScriptContext中的作用域为ENGINE_SCOPE的语言绑定对象。

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

   /**
* 通过脚本执行上下文获取语言绑定对象的示例
*/
public static void useScriptContextValues(){
try{
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
bindings.put("name","Arthur Ming");
engine.eval("print(name)");
} catch (Exception e){
e.printStackTrace();
}
}

执行结果

    自定义属性实际上也保存在语言绑定对象中。

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

    /**
* 自定义属性保存在语言绑定对象中示例
*/
public static void attributeInBindings(){
try{
ScriptEngine engine = getJavaScriptEngine();
ScriptContext context = engine.getContext();
context.setAttribute("name1","Arthur Ming",ScriptContext.GLOBAL_SCOPE);
context.setAttribute("name2","明国宾",ScriptContext.ENGINE_SCOPE);
engine.eval("print(name1);");
engine.eval("print(name2);");
}catch (Exception e){
e.printStackTrace();
}
}

执行结果

Java学习笔记--脚本语言支持API的更多相关文章

  1. Java学习笔记:语言基础

    Java学习笔记:语言基础 2014-1-31   最近开始学习Java,目的倒不在于想深入的掌握Java开发,而是想了解Java的基本语法,可以阅读Java源代码,从而拓展一些知识面.同时为学习An ...

  2. 【Todo】Java学习笔记 100==100 & Reflection API & Optional类详解 & DIP、IoC、DI & token/cookie/session管理会话方式

    为什么1000 == 1000返回为False,而100 == 100会返回为True?   Link Java Reflection API:Link Java8 Optional 类深度解析: L ...

  3. 【Java学习笔记】其他对象API

    System类 package p1; import java.util.Properties; import java.util.Set; public class SystemDemo { pri ...

  4. Java学习笔记之---API的应用

    Java学习笔记之---API的应用 (一)Object类 java.lang.Object 类 Object 是类层次结构的根类.每个类都使用 Object 作为超类.所有对象(包括数组)都实现这个 ...

  5. [java学习笔记]java语言核心----面向对象之this关键字

    一.this关键字 体现:当成员变量和函数的局部变量重名时,可以使用this关键字来区别:在构造函数中调用其它构造函数 原理:         代表的是当前对象.         this就是所在函数 ...

  6. [java学习笔记]java语言核心----面向对象之构造函数

    1.构造函数概念 特点: 函数名与类名相同 不用定义返回值类型 没有具体的返回值 作用:                给对象进行初始化 注意: 默认构造函数 多个构造函数是以重载出现的 一个类中如果 ...

  7. Java学习笔记4

    Java学习笔记4 1. JDK.JRE和JVM分别是什么,区别是什么? 答: ①.JDK 是整个Java的核心,包括了Java运行环境.Java工具和Java基础类库. ②.JRE(Java Run ...

  8. 《Java学习笔记(第8版)》学习指导

    <Java学习笔记(第8版)>学习指导 目录 图书简况 学习指导 第一章 Java平台概论 第二章 从JDK到IDE 第三章 基础语法 第四章 认识对象 第五章 对象封装 第六章 继承与多 ...

  9. 20145330第十周《Java学习笔记》

    20145330第十周<Java学习笔记> 网络编程 网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据.程序员所作的事情就是把数据发送到指定的位置,或者接收到指定的数据,这个就 ...

随机推荐

  1. vue视频学习笔记02

    video 2 vue制作weibo交互 vue-> 1.0vue-resource ajax php服务器环境(node) this.$http.get()/post()/jsonp() th ...

  2. Kafka权威指南——broker的常用配置

    前面章节中的例子,用来作为单个节点的服务器示例是足够的,但是如果想要把它应用到生产环境,就远远不够了.在Kafka中有很多参数可以控制它的运行和工作.大部分的选项都可以忽略直接使用默认值就好,遇到一些 ...

  3. Oracle 12C 新特性之表分区带 异步全局索引异步维护(一次add、truncate、drop、spilt、merge多个分区)

    实验准备:-- 创建实验表CREATE TABLE p_andy(ID number(10), NAME varchar2(40))PARTITION BY RANGE (id)(PARTITION ...

  4. list和map集合

    List特点:元素有放入顺序,元素可重复Set特点:元素无放入顺序,元素不可重复(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的)Map特点: ...

  5. MySQL使用pt-online-change-schema工具在线修改1.6亿级数据表结构

    摘  要:本文阐述了MySQL DDL 的问题现状.pt-online-schema-change的工作原理,并实际利用pt-online-schema-change工具在线修改生产环境下1.6亿级数 ...

  6. Android 真机无线调试

    有很多人在学Android的时候最开始接触的都是模拟机的测试,如果像好的模拟机比如genimotion,次一点的蓝手指,测试都还比较可以.有的也不缺乏是用真机测试.本人开始用华为真机测试,也是一直连线 ...

  7. 深入学习webpack(一)

    深入学习webpack(一) 模块化的相关库和工具已经很多了,包括require.js.sea.js和一些工程化工具webpack.gulp.grant.那么我们该如何选择呢? 其实,我们只需要掌握了 ...

  8. Spring+SpringMVC+MyBatis深入学习及搭建(一)——MyBatis的基础知识

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6812311.html 1.对原生态jdbc程序中问题总结 1.1 jdbc程序 需求:使用jdbc查询mys ...

  9. js算法集合(二) javascript实现斐波那契数列 (兔子数列)

    js算法集合(二)  斐波那契数列 ★ 上一次我跟大家分享一下做水仙花数的算法的思路,并对其扩展到自幂数的算法,这次,我们来对斐波那契数列进行研究,来加深对循环的理解.     Javascript实 ...

  10. Day3-函数及作用域

    一.函数定义:一组代码片段用函数名封装起来,通过函数名的方式调用执行. 特性: 1.减少重复代码 2.使程序易扩展 3.使程序易维护 语法定义: def sayhi(): print("he ...