欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

Java SE 6 新特性: 對(duì)腳本語(yǔ)言的支持

系統(tǒng) 1923 0

2006 年底,Sun 公司發(fā)布了 Java Standard Edition 6(Java SE 6)的最終正式版,代號(hào) Mustang(野馬)。跟 Tiger(Java SE 5)相比,Mustang 在性能方面有了不錯(cuò)的提升。與 Tiger 在 API 庫(kù)方面的大幅度加強(qiáng)相比,雖然 Mustang 在 API 庫(kù)方面的新特性顯得不太多,但是也提供了許多實(shí)用和方便的功能:在腳本,WebService,XML, 編譯器 API 數(shù)據(jù)庫(kù) JMX 網(wǎng)絡(luò) Instrumentation 方面都有不錯(cuò)的新特性和功能加強(qiáng)。

本系列 文章主要介紹 Java SE 6 在 API 庫(kù)方面的部分新特性,通過(guò)一些例子和講解,幫助開(kāi)發(fā)者在編程實(shí)踐當(dāng)中更好的運(yùn)用 Java SE 6,提高開(kāi)發(fā)效率。本文是系列文章的第 6 篇,介紹了 Java SE 6 在腳本編程方面的新特性。

<!----><!----><!---->

Java 腳本 API 概述

腳本引擎
腳本引擎就是指腳本的運(yùn)行環(huán)境,它能能夠把運(yùn)行其上的解釋性語(yǔ)言轉(zhuǎn)換為更底層的匯編語(yǔ)言,沒(méi)有腳本引擎,腳本就無(wú)法被運(yùn)行。

Java SE 6 引入了對(duì) Java Specification Request(JSR)223 的支持, JSR 223 旨在定義一個(gè)統(tǒng)一的規(guī)范,使得 Java 應(yīng)用程序可以通過(guò)一套固定的接口與各種腳本引擎交互,從而達(dá)到在 Java 平臺(tái)上調(diào)用各種腳本語(yǔ)言的目的。 javax.script 包定義了這些接口,即 Java 腳本編程 API。Java 腳本 API 的目標(biāo)與 Apache 項(xiàng)目 Bean Script Framework(BSF)類(lèi)似,通過(guò)它 Java 應(yīng)用程序就能通過(guò)虛擬機(jī)調(diào)用各種腳本,同時(shí),腳本語(yǔ)言也能訪問(wèn)應(yīng)用程序中的 Java 對(duì)象和方法。Java 腳本 API 是連通 Java 平臺(tái)和腳本語(yǔ)言的橋梁。首先,通過(guò)它為數(shù)眾多的現(xiàn)有 Java 庫(kù)就能被各種腳本語(yǔ)言所利用,節(jié)省了開(kāi)發(fā)成本縮短了開(kāi)發(fā)周期;其次,可以把一些復(fù)雜異變的業(yè)務(wù)邏輯交給腳本語(yǔ)言處理,這又大大提高了開(kāi)發(fā)效率。

javax.script 包中定義的實(shí)現(xiàn)類(lèi)并不多,主要是一些接口和對(duì)應(yīng)的抽象類(lèi), 圖 1 顯示了其中包含的各個(gè)接口和類(lèi)。


圖 1. javax.script 包概況

這個(gè)包的具體實(shí)現(xiàn)類(lèi)少的根本原因是這個(gè)包只是定義了一個(gè)編程接口的框架規(guī)范,至于對(duì)如何解析運(yùn)行具體的腳本語(yǔ)言,還需要由第三方提供實(shí)現(xiàn)。雖然這些腳本引擎的實(shí)現(xiàn)各不相同,但是對(duì)于 Java 腳本 API 的使用者來(lái)說(shuō),這些具體的實(shí)現(xiàn)被很好的隔離隱藏了。Java 腳本 API 為開(kāi)發(fā)者提供了如下功能:

  1. 獲取腳本程序輸入,通過(guò)腳本引擎運(yùn)行腳本并返回運(yùn)行結(jié)果,這是最核心的接口。
  2. 發(fā)現(xiàn)腳本引擎,查詢腳本引擎信息。
  3. 通過(guò)腳本引擎的運(yùn)行上下文在腳本和 Java 平臺(tái)間交換數(shù)據(jù)。
  4. 通過(guò) Java 應(yīng)用程序調(diào)用腳本函數(shù)。

在詳細(xì)介紹這四個(gè)功能之前,我們先通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)展示如何通過(guò) Java 語(yǔ)言來(lái)運(yùn)行腳本程序,這里仍然以經(jīng)典的“Hello World”開(kāi)始。


清單 1. Hello World
              
import javax.script.*;
public class HelloWorld {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.eval("print ('Hello World')");
}
}

這個(gè)例子非常直觀,只要通過(guò) ScriptEngineManager ScriptEngine 這兩個(gè)類(lèi)就可以完成最簡(jiǎn)單的調(diào)用。首先, ScriptEngineManager 實(shí)例創(chuàng)建一個(gè) ScriptEngine 實(shí)例,然后返回的 ScriptEngine 實(shí)例解析 JavaScript 腳本,輸出運(yùn)行結(jié)果。運(yùn)行這段程序,終端上會(huì)輸出“Hello World“。在執(zhí)行 eval 函數(shù)的過(guò)程中可能會(huì)有 ScriptEngine 異常拋出,引發(fā)這個(gè)異常被拋出的原因一般是由腳本輸入語(yǔ)法有誤造成的。在對(duì)整個(gè) API 有了大致的概念之后,我們就可以開(kāi)始介紹各個(gè)具體的功能了。





回頁(yè)首


使用腳本引擎運(yùn)行腳本

Java 腳本 API 通過(guò)腳本引擎來(lái)運(yùn)行腳本,整個(gè)包的目的就在于統(tǒng)一 Java 平臺(tái)與各種腳本引擎的交互方式,制定一個(gè)標(biāo)準(zhǔn),Java 應(yīng)用程序依照這種標(biāo)準(zhǔn)就能自由的調(diào)用各種腳本引擎,而腳本引擎按照這種標(biāo)準(zhǔn)實(shí)現(xiàn),就能被 Java 平臺(tái)支持。每一個(gè)腳本引擎就是一個(gè)腳本解釋器,負(fù)責(zé)運(yùn)行腳本,獲取運(yùn)行結(jié)果。 ScriptEngine 接口是腳本引擎在 Java 平臺(tái)上的抽象,Java 應(yīng)用程序通過(guò)這個(gè)接口調(diào)用腳本引擎運(yùn)行腳本程序,并將運(yùn)行結(jié)果返回給虛擬機(jī)。

ScriptEngine 接口提供了許多 eval 函數(shù)的變體用來(lái)運(yùn)行腳本,這個(gè)函數(shù)的功能就是獲取腳本輸入,運(yùn)行腳本,最后返回輸出。 清單 1 的例子中直接通過(guò)字符串作為 eval 函數(shù)的參數(shù)讀入腳本程序。除此之外, ScriptEngine 還提供了以一個(gè) java.io.Reader 作為輸入?yún)?shù)的 eval 函數(shù)。腳本程序?qū)嵸|(zhì)上是一些可以用腳本引擎執(zhí)行的字節(jié)流,通過(guò)一個(gè) Reader 對(duì)象, eval 函數(shù)就能從不同的數(shù)據(jù)源中讀取字節(jié)流來(lái)運(yùn)行,這個(gè)數(shù)據(jù)源可以來(lái)自內(nèi)存、文件,甚至直接來(lái)自網(wǎng)絡(luò)。這樣 Java 應(yīng)用程序就能直接利用項(xiàng)目原有的腳本資源,無(wú)需以 Java 語(yǔ)言對(duì)其進(jìn)行重寫(xiě),達(dá)到腳本程序與 Java 平臺(tái)無(wú)縫集成的目的。 清單 2 即展示了如何從一個(gè)文件中讀取腳本程序并運(yùn)行,其中如何通過(guò) ScriptEngineManager 獲取 ScriptEngine 實(shí)例的細(xì)節(jié)會(huì)在后面詳細(xì)介紹。


清單 2. Run Script
              
public class RunScript {

public static void main(String[] args) throws Exception {
String script = args[0];
String file = args[1];

FileReader scriptReader = new FileReader(new File(file));
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName(script);
engine.eval(scriptReader);
}
}

清單 2 代碼,從命令行分別獲取腳本名稱(chēng)和腳本文件名,程序通過(guò)腳本名稱(chēng)創(chuàng)建對(duì)應(yīng)的腳本引擎實(shí)例,通過(guò)腳本名稱(chēng)指定的腳本文件名讀入腳本程序運(yùn)行。運(yùn)行下面這個(gè)命令,就能在 Java 平臺(tái)上運(yùn)行所有的 JavaScript 腳本。

              java RunScript javascript run.js
              

通過(guò)這種方式,Java 應(yīng)用程序可以把一些復(fù)雜易變的邏輯過(guò)程,用更加靈活的弱類(lèi)型的腳本語(yǔ)言來(lái)實(shí)現(xiàn),然后通過(guò) javax.Script 包提供的 API 獲取運(yùn)行結(jié)果,當(dāng)腳本改變時(shí),只需替換對(duì)應(yīng)的腳本文件,而無(wú)需重新編譯構(gòu)建項(xiàng)目,好處是顯而易見(jiàn)的,即節(jié)省了開(kāi)發(fā)時(shí)間又提高了開(kāi)發(fā)效率。

EngineScript 接口分別針對(duì) String 輸入和 Reader 輸入提供了三個(gè)不同形態(tài)的 eval 函數(shù),用于運(yùn)行腳本:


表 1. ScriptEngine 的 eval 函數(shù)
函數(shù) 描述
Object eval(Reader reader) 從一個(gè) Reader 讀取腳本程序并運(yùn)行
Object eval(Reader reader, Bindings n) n 作為腳本級(jí)別的綁定,從一個(gè) Reader 讀取腳本程序并運(yùn)行
Object eval(Reader reader, ScriptContext context) context 指定的上下文環(huán)境下,從一個(gè) Reader 讀取腳本程序并運(yùn)行
Object eval(String script) 運(yùn)行字符串表示的腳本
Object eval(String script, Bindings n) n 作為腳本級(jí)別的綁定,運(yùn)行字符串表示的腳本
Object eval(String script, ScriptContext context) context 指定的上下文環(huán)境下,運(yùn)行字符串表示的腳本

Java 腳本 API 還為 ScriptEngine 接口提供了一個(gè)抽象類(lèi) —— AbstractScriptEngine ,這個(gè)類(lèi)提供了其中四個(gè) eval 函數(shù)的默認(rèn)實(shí)現(xiàn),它們分別通過(guò)調(diào)用 eval(Reader,ScriptContext) eval(String, ScriptContext) 來(lái)實(shí)現(xiàn)。這樣腳本引擎提供者,只需繼承這個(gè)抽象類(lèi)并提供這兩個(gè)函數(shù)實(shí)現(xiàn)即可。 AbstractScriptEngine 有一個(gè)保護(hù)域 context 用于保存默認(rèn)上下文的引用, SimpleScriptContext 類(lèi)被作為 AbstractScriptEngine 的默認(rèn)上下文。關(guān)于上下文環(huán)境,將在后面進(jìn)行詳細(xì)介紹。





回頁(yè)首


發(fā)現(xiàn)和創(chuàng)建腳本引擎

在前面的兩個(gè)例子中, ScriptEngine 實(shí)例都是通過(guò)調(diào)用 ScriptEngineManager 實(shí)例的方法返回的,而不是常見(jiàn)的直接通過(guò) new 操作新建一個(gè)實(shí)例。JSR 223 中引入 ScriptEngineManager 類(lèi)的意義就在于,將 ScriptEngine 的尋找和創(chuàng)建任務(wù)委托給 ScriptEngineManager 實(shí)例處理,達(dá)到對(duì) API 使用者隱藏這個(gè)過(guò)程的目的,使 Java 應(yīng)用程序在無(wú)需重新編譯的情況下,支持腳本引擎的動(dòng)態(tài)替換。通過(guò) ScriptEngineManager 類(lèi)和 ScriptEngineFactory 接口即可完成腳本引擎的發(fā)現(xiàn)和創(chuàng)建:

  • ScriptEngineManager 類(lèi): 自動(dòng)尋找 ScriptEngineFactory 接口的實(shí)現(xiàn)類(lèi)
  • ScriptEngineFactory 接口: 創(chuàng)建合適的腳本引擎實(shí)例
Service Provider
服務(wù)(service)是指那些成為事實(shí)上標(biāo)準(zhǔn)的接口,服務(wù)提供者(service provider)則提供了這個(gè)接口的具體實(shí)現(xiàn)。不同的提供者會(huì)遵循同樣的接口提供實(shí)現(xiàn),客戶可以自由選擇不同的實(shí)現(xiàn)。可以從 Sun 提供的文檔 Jar 文件規(guī)約 中獲取有關(guān) Service Provider 更詳細(xì)的信息。

ScriptEngineManager 類(lèi)本身并不知道如何創(chuàng)建一個(gè)具體的腳本引擎實(shí)例,它會(huì)依照 Jar 規(guī)約中定義的服務(wù)發(fā)現(xiàn)機(jī)制,查找并創(chuàng)建一個(gè)合適的 ScriptEngineFactory 實(shí)例,并通過(guò)這個(gè)工廠類(lèi)來(lái)創(chuàng)建返回實(shí)際的腳本引擎。首先, ScriptEngineManager 實(shí)例會(huì)在當(dāng)前 classpath 中搜索所有可見(jiàn)的 Jar 包;然后,它會(huì)查看每個(gè) Jar 包中的 META -INF/services/ 目錄下的是否包含 javax.script.ScriptEngineFactory 文件,腳本引擎的開(kāi)發(fā)者會(huì)提供在 Jar 包中包含一個(gè) ScriptEngineFactory 接口的實(shí)現(xiàn)類(lèi),這個(gè)文件內(nèi)容即是這個(gè)實(shí)現(xiàn)類(lèi)的完整名字; ScriptEngineManager 會(huì)根據(jù)這個(gè)類(lèi)名,創(chuàng)建一個(gè) ScriptEngineFactory 接口的實(shí)例;最后,通過(guò)這個(gè)工廠類(lèi)來(lái)實(shí)例化需要的腳本引擎,返回給用戶。舉例來(lái)說(shuō),第三方的引擎提供者可能升級(jí)更新了新版的腳本引擎實(shí)現(xiàn),通過(guò) ScriptEngineManager 來(lái)管理腳本引擎,無(wú)需修改一行 Java 代碼就能替換更新腳本引擎。用戶只需在 classpath 中加入新的腳本引擎實(shí)現(xiàn)(Jar 包的形式), ScriptEngineManager 就能通過(guò) Service Provider 機(jī)制來(lái)自動(dòng)查找到新版本實(shí)現(xiàn),創(chuàng)建并返回對(duì)應(yīng)的腳本引擎實(shí)例供調(diào)用。 圖 2 所示時(shí)序圖描述了其中的步驟:


圖 2. 腳本引擎發(fā)現(xiàn)機(jī)制時(shí)序圖

ScriptEngineFactory 接口的實(shí)現(xiàn)類(lèi)被用來(lái)描述和實(shí)例化 ScriptEngine 接口,每一個(gè)實(shí)現(xiàn) ScriptEngine 接口的類(lèi)會(huì)有一個(gè)對(duì)應(yīng)的工廠類(lèi)來(lái)描述其元數(shù)據(jù)(meta data), ScriptEngineFactory 接口定義了許多函數(shù)供 ScriptEngineManager 查詢這些元數(shù)據(jù), ScriptEngineManager 會(huì)根據(jù)這些元數(shù)據(jù)查找需要的腳本引擎, 表 2 列出了可供使用的函數(shù):


表 2. ScriptEngineFactory 提供的查詢函數(shù)
函數(shù) 描述
String getEngineName() 返回腳本引擎的全稱(chēng)
String getEngineVersion() 返回腳本引擎的版本信息
String getLanguageName() 返回腳本引擎所支持的腳本語(yǔ)言的名稱(chēng)
String getLanguageVersion() 返回腳本引擎所支持的腳本語(yǔ)言的版本信息
List<String> getExtensions() 返回一個(gè)腳本文件擴(kuò)展名組成的 List,當(dāng)前腳本引擎支持解析這些擴(kuò)展名對(duì)應(yīng)的腳本文件
List<String> getMimeTypes() 返回一個(gè)與當(dāng)前引擎關(guān)聯(lián)的所有 mimetype 組成的 List
List<String> getNames() 返回一個(gè)當(dāng)前引擎所有名稱(chēng)的 List, ScriptEngineManager 可以根據(jù)這些名字確定對(duì)應(yīng)的腳本引擎

通過(guò) getEngineFactories() 函數(shù), ScriptEngineManager 會(huì)返回一個(gè)包含當(dāng)前環(huán)境中被發(fā)現(xiàn)的所有實(shí)現(xiàn) ScriptEngineFactory 接口的具體類(lèi),通過(guò)這些工廠類(lèi)中保存的腳本引擎信息檢索需要的腳本引擎。第三方提供的腳本引擎實(shí)現(xiàn)的 Jar 包中除了包含 ScriptEngine 接口的實(shí)現(xiàn)類(lèi)之外,還需要提供 ScriptEngineFactory 接口的實(shí)現(xiàn)類(lèi),以及一個(gè) javax.script.ScriptEngineFactory 文件用于指明這個(gè)工廠類(lèi)。這樣,Java 平臺(tái)就能通過(guò) ScriptEngineManager 尋找到這個(gè)工廠類(lèi),并通過(guò)這個(gè)工廠類(lèi)為用戶提供一個(gè)腳本引擎實(shí)例。Java SE 6 默認(rèn)提供了 JavaScirpt 腳本引擎的實(shí)現(xiàn),如果需要支持其他腳本引擎,需要將它們對(duì)應(yīng)的 Jar 包包含在 classpath 中,比如對(duì)于前面 清單 2 中的代碼,只需在運(yùn)行程序前將 Groovy 的腳本引擎添加到 classpath 中,然后運(yùn)行:

              java RunScript groovy run.groovy
              

無(wú)需修改一行 Java 代碼就能以 Groovy 腳本引擎來(lái)運(yùn)行 Groovy 腳本。在 這里 為 Java SE 6 提供了許多著名腳本語(yǔ)言的腳本引擎對(duì) JSR 223 的支持,這些 Jar 必須和腳本引擎配合使用,使得這些腳本語(yǔ)言能被 Java 平臺(tái)支持。到目前為止,它提供了至少 25 種腳本語(yǔ)言的支持,其中包括了 Groovy、Ruby、Python 等當(dāng)前非常流行的腳本語(yǔ)言。這里需要再次強(qiáng)調(diào)的是,負(fù)責(zé)創(chuàng)建 ScriptEngine 實(shí)例的 ScriptEngineFactory 實(shí)現(xiàn)類(lèi)對(duì)于用戶來(lái)說(shuō)是不可見(jiàn)的, ScriptEngingeManager 實(shí)現(xiàn)負(fù)責(zé)與其交互,通過(guò)它創(chuàng)建腳本引擎。





回頁(yè)首


腳本引擎的運(yùn)行上下文

如果僅僅是通過(guò)腳本引擎運(yùn)行腳本的話,還無(wú)法體現(xiàn)出 Java 腳本 API 的優(yōu)點(diǎn),在 JSR 223 中,還為所有的腳本引擎定義了一個(gè)簡(jiǎn)潔的執(zhí)行環(huán)境。我們都知道,在 Linux 操作系統(tǒng)中可以維護(hù)許多環(huán)境變量比如 classpath、path 等,不同的 shell 在運(yùn)行時(shí)可以直接使用這些環(huán)境變量,它們構(gòu)成了 shell 腳本的執(zhí)行環(huán)境。在 javax.script 支持的每個(gè)腳本引擎也有各自對(duì)應(yīng)的執(zhí)行的環(huán)境,腳本引擎可以共享同樣的環(huán)境,也可以有各自不同的上下文。通過(guò)腳本運(yùn)行時(shí)的上下文,腳本程序就能自由的和 Java 平臺(tái)交互,并充分利用已有的眾多 Java API,真正的站在“巨人”的肩膀上。 javax.script.ScriptContext 接口和 javax.script.Bindings 接口定義了腳本引擎的上下文。

  • Bindings 接口:

    繼承自 Map,定義了對(duì)這些“鍵-值”對(duì)的查詢、添加、刪除等 Map 典型操作。 Bingdings 接口實(shí)際上是一個(gè)存放數(shù)據(jù)的容器,它的實(shí)現(xiàn)類(lèi)會(huì)維護(hù)許多“鍵-值”對(duì),它們都通過(guò)字符串表示。Java 應(yīng)用程序和腳本程序通過(guò)這些“鍵-值”對(duì)交換數(shù)據(jù)。只要腳本引擎支持,用戶還能直接在 Bindings 中放置 Java 對(duì)象,腳本引擎通過(guò) Bindings 不僅可以存取對(duì)象的屬性,還能調(diào)用 Java 對(duì)象的方法,這種雙向自由的溝通使得二者真正的結(jié)合在了一起。

  • ScriptContext 接口:

    Bindings ScriptEngine 聯(lián)系在了一起,每一個(gè) ScriptEngine 都有一個(gè)對(duì)應(yīng)的 ScriptContext ,前面提到過(guò)通過(guò) ScriptEnginFactory 創(chuàng)建腳本引擎除了達(dá)到隱藏實(shí)現(xiàn)的目的外,還負(fù)責(zé)為腳本引擎設(shè)置合適的上下文。 ScriptEngine 通過(guò) ScriptContext 實(shí)例就能從其內(nèi)部的 Bindings 中獲得需要的屬性值。 ScriptContext 接口默認(rèn)包含了兩個(gè)級(jí)別的 Bindings 實(shí)例的引用,分別是全局級(jí)別和引擎級(jí)別,可以通過(guò) GLOBAL_SCOPE ENGINE_SCOPE 這兩個(gè)類(lèi)常量來(lái)界定區(qū)分這兩個(gè) Bindings 實(shí)例,其中 GLOBAL_SCOPE 從創(chuàng)建它的 ScriptEngineManager 獲得。顧名思義,全局級(jí)別指的是 Bindings 里的屬性都是“全局變量”,只要是同一個(gè) ScriptEngineMananger 返回的腳本引擎都可以共享這些屬性;對(duì)應(yīng)的,引擎級(jí)別的 Bindings 里的屬性則是“局部變量”,它們只對(duì)同一個(gè)引擎實(shí)例可見(jiàn),從而能為不同的引擎設(shè)置獨(dú)特的環(huán)境,通過(guò)同一個(gè)腳本引擎運(yùn)行的腳本運(yùn)行時(shí)能共享這些屬性。

ScriptContext 接口定義了下面這些函數(shù)來(lái)存取數(shù)據(jù):


表 3. ScriptContext 存取屬性函數(shù)
函數(shù) 描述
Object removeAttribute(String name, int scope) 從指定的范圍里刪除一個(gè)屬性
void setAttribute(String name, Object value, int scope) 在指定的范圍里設(shè)置一個(gè)屬性的值
Object getAttribute(String name) 從上下文的所有范圍內(nèi)獲取優(yōu)先級(jí)最高的屬性的值
Object getAttribute(String name, int scope) 從指定的范圍里獲取屬性值

ScriptEngineManager 擁有一個(gè)全局性的 Bindings 實(shí)例,在通過(guò) ScriptEngineFactory 實(shí)例創(chuàng)建 ScriptEngine 后,它把自己的這個(gè) Bindings 傳遞給所有它創(chuàng)建的 ScriptEngine 實(shí)例,作為 GLOBAL_SCOPE 。同時(shí),每一個(gè) ScriptEngine 實(shí)例都對(duì)應(yīng)一個(gè) ScriptContext 實(shí)例,這個(gè) ScriptContext 除了從 ScriptEngineManager 那獲得的 GLOBAL_SCOPE ,自己也維護(hù)一個(gè) ENGINE_SCOPE Bindings 實(shí)例,所有通過(guò)這個(gè)腳本引擎運(yùn)行的腳本,都能存取其中的屬性。除了 ScriptContext 可以設(shè)置屬性,改變內(nèi)部的 Bindings ,Java 腳本 API 為 ScriptEngineManager ScriptEngine 也提供了類(lèi)似的設(shè)置屬性和 Bindings 的 API。


圖 3. Bindings 在 Java 腳本 API 中的分布

圖 3 中可以看到,共有三個(gè)級(jí)別的地方可以存取屬性,分別是 ScriptEngineManager 中的 Bindings ScriptEngine 實(shí)例對(duì)應(yīng)的 ScriptContext 中含有的 Bindings ,以及調(diào)用 eval 函數(shù)時(shí)傳入的 Bingdings 。離函數(shù)調(diào)用越近,其作用域越小,優(yōu)先級(jí)越高,相當(dāng)于編程語(yǔ)言中的變量的可見(jiàn)域,即 Object getAttribute(String name) 中提到的優(yōu)先級(jí)。從 清單 3 這個(gè)例子中可以看出各個(gè)屬性的存取優(yōu)先級(jí):


清單 3. 上下文屬性的作用域
              
import javax.script.*;

public class ScopeTest {
public static void main(String[] args) throws Exception {
String script=" println(greeting) ";
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");

//Attribute from ScriptEngineManager
manager.put("greeting", "Hello from ScriptEngineManager");
engine.eval(script);

//Attribute from ScriptEngine
engine.put("greeting", "Hello from ScriptEngine");
engine.eval(script);

//Attribute from eval method
ScriptContext context = new SimpleScriptContext();
context.setAttribute("greeting", "Hello from eval method",
ScriptContext.ENGINE_SCOPE);
engine.eval(script,context);

}
}

JavaScript 腳本 println(greeting) 在這個(gè)程序中被重復(fù)調(diào)用了三次,由于三次調(diào)用的環(huán)境不一樣,導(dǎo)致輸出也不一樣, greeting 變量每一次都被優(yōu)先級(jí)更高的也就是距離函數(shù)調(diào)用越近的值覆蓋。從這個(gè)例子同時(shí)也演示了如何使用 ScriptContext Bindings 這兩個(gè)接口,在例子腳本中并沒(méi)有定義 greeting 這個(gè)變量,但是腳本通過(guò) Java 腳本 API 能方便的存取 Java 應(yīng)用程序中的對(duì)象,輸出 greeting 相應(yīng)的值。運(yùn)行這個(gè)程序后,能看到輸出為:


圖 4. 程序 ScopeTest 的輸出

除了能在 Java 平臺(tái)與腳本程序之間的提供共享屬性之外, ScriptContext 還允許用戶重定向引擎執(zhí)行時(shí)的輸入輸出流:


表 4. ScriptContext 輸入輸出重定向
函數(shù) 描述
void setErrorWriter(Writer writer) 重定向錯(cuò)誤輸出,默認(rèn)是標(biāo)準(zhǔn)錯(cuò)誤輸出
void setReader(Reader reader) 重定向輸入,默認(rèn)是標(biāo)準(zhǔn)輸入
void setWriter(Writer writer) 重定向輸出,默認(rèn)是標(biāo)準(zhǔn)輸出
Writer getErrorWriter() 獲取當(dāng)前錯(cuò)誤輸出字節(jié)流
Reader getReader() 獲取當(dāng)前輸入流
Writer getWriter() 獲取當(dāng)前輸出流

清單 4 展示了如何通過(guò) ScriptContext 將其對(duì)應(yīng)的 ScriptEngine 標(biāo)準(zhǔn)輸出重定向到一個(gè) PrintWriter 中,用戶可以通過(guò)與這個(gè) PrintWriter 連通的 PrintReader 讀取實(shí)際的輸出,使 Java 應(yīng)用程序能獲取腳本運(yùn)行輸出,滿足更加多樣的應(yīng)用需求。


清單 4. 重定向腳本輸出
              
import java.io.*;
import javax.script.*;

public class Redirectory {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");

PipedReader pr = new PipedReader();
PipedWriter pw = new PipedWriter(pr);
PrintWriter writer = new PrintWriter(pw);
engine.getContext().setWriter(writer);

String script = "println('Hello from JavaScript')";
engine.eval(script);

BufferedReader br =new BufferedReader(pr);
System.out.println(br.readLine());
}
}

Java 腳本 API 分別為這兩個(gè)接口提供了一個(gè)簡(jiǎn)單的實(shí)現(xiàn)供用戶使用。 SimpleBindings 通過(guò)組合模式實(shí)現(xiàn) Map 接口,它提供了兩個(gè)構(gòu)造函數(shù)。無(wú)參構(gòu)造函數(shù)在內(nèi)部構(gòu)造一個(gè) HashMap 實(shí)例來(lái)實(shí)現(xiàn) Map 接口要求的功能;同時(shí), SimpleBindings 也提供了一個(gè)以 Map 接口作為參數(shù)的構(gòu)造函數(shù),允許任何實(shí)現(xiàn) Map 接口的類(lèi)作為其組合的實(shí)例,以滿足不同的要求。 SimpleScriptContext 提供了 ScriptContext 簡(jiǎn)單實(shí)現(xiàn)。默認(rèn)情況下,它使用了標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯(cuò)誤輸出,同時(shí)維護(hù)一個(gè) SimpleBindings 作為其引擎級(jí)別的 Bindings,它的默認(rèn)全局級(jí)別 Bindings 為空。





回頁(yè)首


腳本引擎可選的接口

在 Java 腳本 API 中還有兩個(gè)腳本引擎可以選擇是否實(shí)現(xiàn)的接口,這個(gè)兩個(gè)接口不是強(qiáng)制要求實(shí)現(xiàn)的,即并非所有的腳本引擎都能支持這兩個(gè)函數(shù),不過(guò) Java SE 6 自帶的 JavaScript 引擎支持這兩個(gè)接口。無(wú)論如何,這兩個(gè)接口提供了非常實(shí)用的功能,它們分別是:
  • Invocable 接口: 允許 Java 平臺(tái)調(diào)用腳本程序中的函數(shù)或方法。
  • Compilable 接口: 允許 Java 平臺(tái)編譯腳本程序,供多次調(diào)用。

Invocable 接口

有時(shí)候,用戶可能并不需要運(yùn)行已有的整個(gè)腳本程序,而僅僅需要調(diào)用其中的一個(gè)過(guò)程,或者其中某個(gè)對(duì)象的方法,這個(gè)時(shí)候 Invocable 接口就能發(fā)揮作用。它提供了兩個(gè)函數(shù) invokeFunction invokeMethod ,分別允許 Java 應(yīng)用程序直接調(diào)用腳本中的一個(gè)全局性的過(guò)程以及對(duì)象中的方法,調(diào)用后者時(shí),除了指定函數(shù)名字和參數(shù)外,還需要傳入要調(diào)用的對(duì)象引用,當(dāng)然這需要腳本引擎的支持。不僅如此, Invocable 接口還允許 Java 應(yīng)用程序從這些函數(shù)中直接返回一個(gè)接口,通過(guò)這個(gè)接口實(shí)例來(lái)調(diào)用腳本中的函數(shù)或方法,從而我們可以從腳本中動(dòng)態(tài)的生成 Java 應(yīng)用中需要的接口對(duì)象。 清單 5 演示了如何使用一個(gè) Invocable 接口:


清單 5. 調(diào)用腳本中的函數(shù)
              
import javax.script.*;

public class CompilableTest {
public static void main(String[] args) throws ScriptException,
NoSuchMethodException {
String script = " function greeting(message){println (message);}";
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
engine.eval(script);

if (engine instanceof Invocable) {
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("greeting", "hi");

// It may through NoSuchMethodException
try {
invocable.invokeFunction("nogreeing");
} catch (NoSuchMethodException e) {
// expected
}
}
}
}

在調(diào)用函數(shù)前,可以先通過(guò) instanceof 操作判斷腳本引擎是否支持編譯操作,防止類(lèi)型轉(zhuǎn)換時(shí)拋出運(yùn)行時(shí)異常,需要特別注意的時(shí),如果調(diào)用了腳本程序中不存在的函數(shù)時(shí),運(yùn)行時(shí)會(huì)拋出一個(gè) NoSuchMethodException 的異常,實(shí)際開(kāi)發(fā)中應(yīng)該注意處理這種特殊情況。

Compilable 接口

一般來(lái)說(shuō),腳本語(yǔ)言都是解釋型的,這也是腳本語(yǔ)言區(qū)別與編譯語(yǔ)言的一個(gè)特點(diǎn),解釋性意味著腳本隨時(shí)可以被運(yùn)行,開(kāi)發(fā)者可以邊開(kāi)發(fā)邊查看接口,從而省 去了編譯這個(gè)環(huán)節(jié),提供了開(kāi)發(fā)效率。但是這也是一把雙刃劍,當(dāng)腳本規(guī)模變大,重復(fù)解釋一段穩(wěn)定的代碼又會(huì)帶來(lái)運(yùn)行時(shí)的開(kāi)銷(xiāo)。有些腳本引擎支持將腳本運(yùn)行編 譯成某種中間形式,這取決與腳本語(yǔ)言的性質(zhì)以及腳本引擎的實(shí)現(xiàn),可以是一些操作碼,甚至是 Java 字節(jié)碼文件。實(shí)現(xiàn)了這個(gè)接口的腳本引擎能把輸入的腳本預(yù)編譯并緩存,從而提高多次運(yùn)行相同腳本的效率。

Java 腳本 API 還為這個(gè)中間形式提供了一個(gè)專(zhuān)門(mén)的類(lèi),每次調(diào)用 Compilable 接口的編譯函數(shù)都會(huì)返回一個(gè) CompiledScript 實(shí)例。 CompiledScript 類(lèi)被用來(lái)保存編譯的結(jié)果,從而能重復(fù)調(diào)用腳本而沒(méi)有重復(fù)解釋的開(kāi)銷(xiāo),實(shí)際效率提高的多少取決于中間形式的徹底程度,其中間形式越接近低級(jí)語(yǔ)言,提高的效率就越高。每一個(gè) CompiledScript 實(shí)例對(duì)應(yīng)于一個(gè)腳本引擎實(shí)例,一個(gè)腳本引擎實(shí)例可以含有多個(gè) CompiledScript (這很容易理解),調(diào)用 CompiledScript eval 函數(shù)會(huì)傳遞給這個(gè)關(guān)聯(lián)的 ScriptEngine 的 eval 函數(shù)。關(guān)于 CompiledScript 類(lèi)需要注意的是,它運(yùn)行時(shí)對(duì)與之對(duì)應(yīng)的 ScriptEngine 狀態(tài)的改變可能會(huì)傳遞給下一次調(diào)用,造成運(yùn)行結(jié)果的不一致。 清單 6 演示了如何使用 Compiable 接口來(lái)調(diào)用腳本:


清單 6. 編譯腳本
              
import javax.script.*;

public class CompilableTest {
public static void main(String[] args) throws ScriptException {
String script = " println (greeting); greeting= 'Good Afternoon!' ";
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("javascript");
engine.put("greeting", "Good Morning!");

if (engine instanceof Compilable) {
Compilable compilable = (Compilable) engine;
CompiledScript compiledScript = compilable.compile(script);
compiledScript.eval();
compiledScript.eval();
}
}
}

InovcableTest 類(lèi)似,也應(yīng)該先通過(guò) instanceof 操作判斷腳本引擎是否支持編譯操作,防止預(yù)料外的異常拋出。并且我們可以發(fā)現(xiàn)同一段編譯過(guò)的腳本,在第二次運(yùn)行時(shí) greeting 變量的內(nèi)容被上一次的運(yùn)行改變了,導(dǎo)致輸出不一致:


圖 5. 程序 CompilableTest 的輸出





回頁(yè)首


jrunscript 工具

Java SE 6 還為運(yùn)行腳本添加了一個(gè)專(zhuān)門(mén)的工具 —— jrunscript。jrunscript 支持兩種運(yùn)行方式:一種是交互式,即邊讀取邊解析運(yùn)行,這種方式使得用戶可以方便調(diào)試腳本程序,馬上獲取預(yù)期結(jié)果;還有一種就是批處理式,即讀取并運(yùn)行整 個(gè)腳本文件。用戶可以把它想象成一個(gè)萬(wàn)能腳本解釋器,即它可以運(yùn)行任意腳本程序,而且它還是跨平臺(tái)的,當(dāng)然所有這一切都有一個(gè)前提,那就是必須告訴它相應(yīng) 的腳本引擎的位置。默認(rèn)即支持的腳本是 JavaScript,這意味著用戶可以無(wú)需任何設(shè)置,通過(guò) jrunscript 在任何支持 Java 的平臺(tái)上運(yùn)行任何 JavaScript 腳本;如果想運(yùn)行其他腳本,可以通過(guò) -l 指定以何種腳本引擎運(yùn)行腳本。不過(guò)這個(gè)工具仍是實(shí)驗(yàn)性質(zhì)的,不一定會(huì)包含在 Java 的后續(xù)版本中,無(wú)論如何,它仍是一個(gè)非常有用的工具。





回頁(yè)首


結(jié)束語(yǔ)

在 Java 平臺(tái)上使用腳本語(yǔ)言編程非常方便,因?yàn)?Java 腳本 API 相對(duì)其他包要小很多。通過(guò) javax.script 包提供的接口和類(lèi)我們可以很方便為我們的 Java 應(yīng)用程序添加對(duì)腳本語(yǔ)言的支持。開(kāi)發(fā)者只要遵照 Java 腳本 API 開(kāi)發(fā)應(yīng)用程序,開(kāi)發(fā)中就無(wú)需關(guān)注具體的腳本語(yǔ)言細(xì)節(jié),應(yīng)用程序就可以動(dòng)態(tài)支持任何符合 JSR 223 標(biāo)準(zhǔn)的腳本語(yǔ)言,不僅如此,只要按照 JSR 223 標(biāo)準(zhǔn)開(kāi)發(fā),用戶甚至還能為 Java 平臺(tái)提供一個(gè)自定義腳本語(yǔ)言的解釋器。在 Java 平臺(tái)上運(yùn)行自己的腳本語(yǔ)言,這對(duì)于眾多開(kāi)發(fā)者來(lái)說(shuō)都是非常有誘惑力的。

Java SE 6 新特性: 對(duì)腳本語(yǔ)言的支持


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號(hào)聯(lián)系: 360901061

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對(duì)您有幫助就好】

您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長(zhǎng)會(huì)非常 感謝您的哦!!!

發(fā)表我的評(píng)論
最新評(píng)論 總共0條評(píng)論
主站蜘蛛池模板: 九九热线精品视频6一 | 免费看一级毛片 | 日韩欧美一区二区三区免费观看 | 色五月丁香五月综合五月 | 日本久久综合网 | 久久vs国产综合色大全 | 亚洲精品久久久久久中文字幕小说 | 黑人精品欧美一区二区蜜桃 | 午夜社区 | 亚洲伦理中文字幕 | 亚洲国产网站 | 久久草在线视频国产一 | 91精品国产综合久久久久久 | 欧美亚洲视频在线观看 | 亚洲精品黄色 | 在线无码 | 日本在线无| 福利四区 | 美女视频黄a视频免费全过程 | 国产一区二区精品久久91 | 一区二区精品 | 91精品网| 亚洲欧美偷拍自拍 | 2021年无线乱码播放高清完整 | 欧美 日韩 | 久久99国产伦子精品免费 | 欧美一级做a爰片免费视频 亚洲男人的天堂久久精品 色吟av | 亚洲在线一区二区三区 | 欧美一级片在线看 | 午夜国产亚洲精品一区 | 六月色婷 | 日韩一区二区在线视频 | 日本一区二区三区免费观看 | 日本久久久久久 | 色综合天天综合网国产成人网 | 午夜视频在线网站 | 亚洲影视在线观看 | 国产午夜亚洲精品一区 | 96国产xxxx| 久久国产精品久久久久久久久久 | 污污成人一区二区三区四区 |