深入了解Java 腳本化api編程
Java腳本化API為誰(shuí)準(zhǔn)備?
腳本語(yǔ)言的一些有用的特性是:
- 方便:大多數(shù)腳本語(yǔ)言都是動(dòng)態(tài)類(lèi)型的。您通??梢詣?chuàng)建新的變量,而不聲明變量類(lèi)型,并且您可以重用變量來(lái)存儲(chǔ)不同類(lèi)型的對(duì)象。此外,腳本語(yǔ)言往往會(huì)自動(dòng)執(zhí)行許多類(lèi)型的轉(zhuǎn)換,例如, 必要時(shí) 將數(shù)字10轉(zhuǎn)換為“10”。
- 開(kāi)發(fā)快速原型:您可以避免編輯編譯運(yùn)行周期,只使用“編輯運(yùn)行”!
- 應(yīng)用擴(kuò)展/定制:你可以“具體化”的部分應(yīng)用程序,例如一些配置腳本,業(yè)務(wù)邏輯/規(guī)則和財(cái)務(wù)應(yīng)用中的數(shù)學(xué)表達(dá)式 。
- 為應(yīng)用添加命令行模式,用于調(diào)試、運(yùn)行時(shí)配置/部署時(shí)間?,F(xiàn)在大多數(shù)應(yīng)用程序都有一個(gè)基于Web的GUI配置工具。但是系統(tǒng)管理員/部署人員常常喜歡命令行工具。一個(gè)“標(biāo)準(zhǔn)”的腳本語(yǔ)言可以用來(lái)實(shí)現(xiàn)這個(gè)目的,而不是發(fā)明特設(shè)的腳本語(yǔ)言。
Java 腳本 API 是一種獨(dú)立于框架的腳本語(yǔ)言,使用來(lái)自于Java代碼的腳本引擎 。通過(guò)java腳本API,可以使用Java語(yǔ)言編寫(xiě)定制/可擴(kuò)展的應(yīng)用程序并將自定義腳本語(yǔ)言選擇留給最終用戶 。Java 應(yīng)用程序開(kāi)發(fā)者不需要在開(kāi)發(fā)過(guò)程中選擇擴(kuò)展語(yǔ)言。如果你使用JSR-223 API來(lái)編寫(xiě)應(yīng)用,那么你的用戶可以使用任何JSR-223兼容的腳本語(yǔ)言。
腳本包
Java 腳本功能是在 javax.script 包中。這是一個(gè)比較小的,簡(jiǎn)單的API。腳本的出發(fā)點(diǎn)是 ScriptEngineManager 類(lèi)。一個(gè) ScriptEngineManager 對(duì)象可以通過(guò)jar文件的服務(wù)發(fā)現(xiàn)機(jī)制發(fā)現(xiàn)腳本引擎。它也可以實(shí)例化腳本引擎來(lái)解釋使用特定的腳本語(yǔ)言編寫(xiě)的腳本。使用腳本編程接口的最簡(jiǎn)單的方法如下:
1.創(chuàng)建一個(gè) ScriptEngineManager 對(duì)象
2.從 ScriptEngineManager 獲取 ScriptEngine 對(duì)象
3.使用 ScriptEngine的eval方法執(zhí)行腳本
現(xiàn)在,是時(shí)候看一些樣本代碼了。了解一些JavaScript有助于閱讀這些例子,但不是強(qiáng)制的。
實(shí)例
“Hello,World”
從ScriptEngineManager實(shí)例中,我們通過(guò) getEngineByName 方法得到一個(gè)JavaScript引擎實(shí)例。通過(guò)腳本引擎的eval方法來(lái)執(zhí)行給定的JavaScript代碼。為簡(jiǎn)便起見(jiàn),本例以及隨后的例子中,我們不對(duì)異常進(jìn)行處理。javax.script API有檢查和運(yùn)行時(shí)異常,你必須妥善處理異常。
import javax.script.*;
public class EvalScript {
public static void main(String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// evaluate JavaScript code from String
engine.eval("print('Hello, World')");
}
}
執(zhí)行一個(gè)腳本文件
在這個(gè)例子中,我們調(diào)用eval方法來(lái)接收java.io.Reader作為輸入源。讀入的腳本被執(zhí)行。這種方式能夠成文件執(zhí)行腳本,用相關(guān)的輸入流對(duì)象讀取URL和資源。
import javax.script.*;
public class EvalFile {
public static void main(String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// evaluate JavaScript code from given file - specified by first argument
engine.eval(new java.io.FileReader(args[0]));
}
}
假設(shè)我們有一個(gè)叫”test.js”的文件,里面的內(nèi)容如下:
println("This is hello from test.js");
我們可以使用下面的方式來(lái)運(yùn)行剛剛的腳本
java EvalFile test.js
腳本變量
當(dāng)你的java應(yīng)用程序嵌入腳本引擎和腳本,你可能希望將您的應(yīng)用程序?qū)ο鬄槿肿兞勘┞队谀_本中。這個(gè)例子演示了如何將您的應(yīng)用程序?qū)ο笞鳛槿肿兞勘┞队谀_本中。我們?cè)趹?yīng)用程序中創(chuàng)建一個(gè) java.io.File對(duì)象作為全局變量,名稱是file。該腳本可以訪問(wèn)變量,例如,它可以調(diào)用它的公共方法。注意訪問(wèn)java對(duì)象、領(lǐng)域和方法的語(yǔ)法依賴于腳本語(yǔ)言。JavaScript支持最“自然”的類(lèi)似java的語(yǔ)法。
public class ScriptVars {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
File f = new File("test.txt");
// expose File object as variable to script
engine.put("file", f);
// evaluate a script string. The script accesses "file"
// variable and calls method on it
engine.eval("print(file.getAbsolutePath())");
}
}
調(diào)用腳本函數(shù)和方法
有些時(shí)候,你可能需要多次調(diào)用一個(gè)特定腳本函數(shù),例如你的應(yīng)用程序菜單功能可能由腳本來(lái)實(shí)現(xiàn)。在菜單中的操作事件處理程序中,可能需要調(diào)用一個(gè)特定的腳本函數(shù)。下面的示例演示在Java代碼調(diào)用一個(gè)特定的腳本。
import javax.script.*;
public class InvokeScriptFunction {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String
String script = "function hello(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
Invocable inv = (Invocable) engine;
// invoke the global function named "hello"
inv.invokeFunction("hello", "Scripting!!" );
}
}
如果你的腳本語(yǔ)言是基于對(duì)象(如JavaScript)或面向?qū)ο蟮?,你可以在腳本對(duì)象上調(diào)用腳本方法。
import javax.script.*;
public class InvokeScriptMethod {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String. This code defines a script object 'obj'
// with one method called 'hello'.
String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
Invocable inv = (Invocable) engine;
// get script object on which we want to call the method
Object obj = engine.get("obj");
// invoke the method named "hello" on the script object "obj"
inv.invokeMethod(obj, "hello", "Script Method !!" );
}
}
通過(guò)腳本實(shí)現(xiàn)Java接口
有些時(shí)候通過(guò)腳本函數(shù)或者方法可以很方便的實(shí)現(xiàn)java接口,而不是在Java中調(diào)用。同時(shí),通過(guò)接口我們可以避免在很多地方使用javax.script API接口。我們可以得到一個(gè)接口實(shí)現(xiàn)者對(duì)象并將其傳遞給不同的Java api。下面的例子演示了通過(guò)腳本實(shí)現(xiàn) java.lang.Runnable接口。
import javax.script.*;
public class RunnableImpl {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String
String script = "function run() { println('run called'); }";
// evaluate script
engine.eval(script);
Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script functions with the matching name.
Runnable r = inv.getInterface(Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
Thread th = new Thread(r);
th.start();
}
}
如果你的腳本語(yǔ)言是基于對(duì)象或者面向?qū)ο蟮?,可以通過(guò)腳本對(duì)象的腳本方法來(lái)實(shí)現(xiàn)Java接口。這避免了不得不調(diào)用腳本全局函數(shù)的接口方法。腳本對(duì)象可以存儲(chǔ)接口實(shí)現(xiàn)狀態(tài)。
import javax.script.*;
public class RunnableImplObject {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String
String script = "var obj = new Object(); obj.run = function() { println('run method called'); }";
// evaluate script
engine.eval(script);
// get script object on which we want to implement the interface with
Object obj = engine.get("obj");
Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script methods of object 'obj'
Runnable r = inv.getInterface(obj, Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
Thread th = new Thread(r);
th.start();
}
}
腳本的多作用域
在 script variables 例子中,我們看到怎樣將應(yīng)用對(duì)象暴露為腳本的全局變量。它有可能暴露為多個(gè)全局的作用域 。 單作用域是javax.script.Bindings的實(shí)例中. 這個(gè)借口派生至java.util.Map<String, Object>。 scope 鍵值對(duì)的集合,其中鍵為非空、非空字符串。 多scopes 是 javax.script.ScriptContext 接口支持的。支持一個(gè)或多個(gè)腳本上下文與相關(guān)的域綁定。默認(rèn)情況下, 每一個(gè)腳本引擎都有一個(gè)默認(rèn)的腳本上下文。 默認(rèn)的腳本上下文有至少一個(gè)域叫 ”ENGINE_SCOPE”。不同域的腳本上下文支持可以通過(guò) getscopes 方法獲取。
import javax.script.*;
public class MultiScopes {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.put("x", "hello");
// print global variable "x"
engine.eval("println(x);");
// the above line prints "hello"
// Now, pass a different script context
ScriptContext newContext = new SimpleScriptContext();
Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
// add new variable "x" to the new engineScope
engineScope.put("x", "world");
// execute the same script - but this time pass a different script context
engine.eval("println(x);", newContext);
// the above line prints "world"
}
}
JavaScript 腳本引擎
Sun的JDK 6中包含了一個(gè)基于 Mozilla Rhino JavaScript 腳本引擎。 這個(gè)引擎是基于版本為1.6R2的Mozilla Rhino 。多數(shù) Rhino 實(shí)現(xiàn)都被包含在內(nèi)。少部分組件由于大小和安全原因被排除了:
1.JavaScript轉(zhuǎn)字節(jié)碼編譯 (也稱 ”優(yōu)化器”).。此功能依賴一個(gè)類(lèi)生成庫(kù)。 去掉本功能意味著:JavaScript是解釋執(zhí)行,且不影響腳本執(zhí)行,因?yàn)閮?yōu)化器是透明的。
2.Rhino的JavaAdapter 也被去掉了。 JavaAdapter是一個(gè)JavaScript可擴(kuò)展Java類(lèi)和JavaScript可實(shí)現(xiàn)Java接口功能。此功能也是需要類(lèi)生成庫(kù)的。我們把Rhino的JavaAdapter替換為Sun實(shí)現(xiàn)的JavaAdapter。在Sun的實(shí)現(xiàn)中,僅僅實(shí)現(xiàn)了JavaScript對(duì)象可實(shí)現(xiàn)Java單接口功能。例如,下面的代碼會(huì)正確執(zhí)行。
var v = new java.lang.Runnable() {
run: function() { print('hello'); }
}
v.run();
在大多數(shù)情況下,JavaAdapter是采用匿名類(lèi)語(yǔ)法來(lái)實(shí)現(xiàn)單接口。 使用JavaAdapter來(lái)擴(kuò)展Java類(lèi)或?qū)崿F(xiàn)多接口并不常見(jiàn)。
3.E4X (ECMAScript for XML – ECMA Standard 357) 被去掉了. 使用XML JavaScript代碼會(huì)產(chǎn)生一個(gè)語(yǔ)法錯(cuò)誤. 請(qǐng)注意,E4X支持ECMAScript標(biāo)準(zhǔn)是可選的-省略E4X的實(shí)現(xiàn)是被支持也是兼容 ECMAScript 。
4.Rhino的命令行工具 (Rhino shell, debugger 等) 沒(méi)有被包含在內(nèi)。但你可以用使用 jrunscript來(lái)代替。
JavaScript與Java的通信
在大多數(shù)情況下,訪問(wèn)Java類(lèi)、對(duì)象和方法很簡(jiǎn)單。從JavaScript中訪問(wèn)屬性和方法與同Java中一樣。這里,我們突出JavaScript Java訪問(wèn)的重要方面.。更多的細(xì)節(jié)請(qǐng)閱讀http://www.mozilla.org/rhino/scriptjava.html。下面是一些JavaScript訪問(wèn)Java的代碼片段。本節(jié)需要一些JavaScript知識(shí)。如果你打算使用JSR-223中非JavaScript腳本語(yǔ)言,那么本節(jié)可以跳過(guò)。
引入Java 包, 類(lèi)
內(nèi)置的函數(shù)importPackage 和importClass 可以用于引入Java 包和類(lèi)。
// Import Java packages and classes
// like import package.*; in Java
importPackage(java.awt);
// like import java.awt.Frame in Java
importClass(java.awt.Frame);
// Create Java Objects by "new ClassName"
var frame = new java.awt.Frame("hello");
// Call Java public methods from script
frame.setVisible(true);
// Access "JavaBean" properties like "fields"
print(frame.title);
全局變量Packages也可以用于訪問(wèn)Java包。
例如: Packages.java.util.Vector, Packages.javax.swing.JFrame. 請(qǐng)注意: ”java” 是 “Packages.java”的快捷引用。還有一些等價(jià)的快捷引用前綴 : javax, org, edu, com, net, 所以幾乎所有的 JDK 平臺(tái)下的類(lèi)都可以不使用”P(pán)ackages” 前綴而訪問(wèn)到。
請(qǐng)注意,java.lang不是默認(rèn)引入的 (與Java不同),因?yàn)闀?huì)與 JavaScript's 內(nèi)置的 Object, Boolean, Math 等沖突。importPackage 和importClass 函數(shù)”污染” 了JavaScript中的全局變量。為了避免這種情況,你可以使用JavaImporter。
// create JavaImporter with specific packages and classes to import
var SwingGui = new JavaImporter(javax.swing,
javax.swing.event,
javax.swing.border,
java.awt.event);
with (SwingGui) {
// within this 'with' statement, we can access Swing and AWT
// classes by unqualified (simple) names.
var mybutton = new JButton("test");
var myframe = new JFrame("test");
}
C創(chuàng)建和使用Java的數(shù)組
在JavaScript中,創(chuàng)建一個(gè)對(duì)象時(shí)與Java中一樣,而創(chuàng)建Java數(shù)組時(shí)需要顯式的使用Java反射。但一旦創(chuàng)建好后,訪問(wèn)其中的元素或獲取大小就和Java中一樣。 另外,也可以使用腳本數(shù)組用在Java方法中期望的Java數(shù)組(因?yàn)榭梢宰詣?dòng)轉(zhuǎn)換)。所以在大多數(shù)情況下我們不需要顯式地創(chuàng)建Java數(shù)組。
// create Java String array of 5 elements var a = java.lang.reflect.Array.newInstance(java.lang.String, 5); // Accessing elements and length access is by usual Java syntax a[0] = "scripting is great!"; print(a.length);
實(shí)現(xiàn)Java 接口
在JavaScript中,可以使用Java匿名類(lèi)語(yǔ)法形式實(shí)現(xiàn)Java中接口:
var r = new java.lang.Runnable() {
run: function() {
print("running...\n");
}
};
// "r" can be passed to Java methods that expect java.lang.Runnable
var th = new java.lang.Thread(r);
th.start();
當(dāng)接口中只有一個(gè)需要實(shí)現(xiàn)的方法時(shí),你可以自己傳入腳本的函數(shù)(因?yàn)榭梢宰詣?dòng)轉(zhuǎn)換)。
function func() {
print("I am func!");
}
// pass script function for java.lang.Runnable argument
var th = new java.lang.Thread(func);
th.start();
重載
Java方法是使用參數(shù)類(lèi)型重載的。在Java中,重載發(fā)生在編譯階段 (執(zhí)行 javac)。當(dāng)腳本中調(diào)用Java方法時(shí),腳本的翻譯器或編譯器需要選擇適當(dāng)?shù)姆椒?。?duì)于JavaScript引擎,您不需要做任何特別的——正確的Java方法重載變體是根據(jù)參數(shù)類(lèi)型選擇的。 但有時(shí),您可能希望(或有)顯式地選擇一個(gè)特定的過(guò)載變體。
var out = java.lang.System.out;
// select a particular println function
out["println(java.lang.Object)"]("hello");
自定義腳本引擎
我們不會(huì)覆蓋的JSR-223兼容腳本引擎實(shí)現(xiàn)細(xì)節(jié). 至少, 您需要實(shí)現(xiàn)javax.script.ScriptEngine 和javax.script.ScriptEngineFactory 接口。
抽象類(lèi)javax.script.AbstractScriptEngine 提供了一些ScriptEngine 接口中定義的方法。
在開(kāi)始實(shí)現(xiàn) JSR-223 引擎之前,您可能需要下載 http://scripting.dev.java.net 工程。這個(gè)工程維護(hù)了一些流行的開(kāi)源腳本語(yǔ)言的 JSR-223 實(shí)現(xiàn)。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
UniApp?+?SpringBoot?實(shí)現(xiàn)微信支付和退款功能
這篇文章主要介紹了UniApp?+?SpringBoot?實(shí)現(xiàn)微信支付和退款功能,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
如何把VS Code打造成Java開(kāi)發(fā)IDE
這篇文章主要介紹了如何把VS Code打造成Java開(kāi)發(fā)IDE,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
MyBatis驗(yàn)證多級(jí)緩存及 Cache Aside 模式的應(yīng)用小結(jié)
本文介紹了MyBatis的多級(jí)緩存機(jī)制,包括本地緩存和全局緩存,并通過(guò)Spock測(cè)試框架驗(yàn)證了多級(jí)緩存的實(shí)現(xiàn),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-12-12
Java中HashMap和Hashtable的區(qū)別淺析
這篇文章主要介紹了Java中HashMap和Hashtable的區(qū)別淺析,本文總結(jié)了6條它們之間的不同之處,需要的朋友可以參考下2015-03-03
Springboot訪問(wèn)html頁(yè)面的教程詳解
這篇文章主要介紹了Springboot訪問(wèn)html頁(yè)面的教程,本文圖文并茂給大家介紹的非常詳細(xì),需要的朋友可以參考下2018-03-03
Spring?Boot中的過(guò)濾器攔截器監(jiān)聽(tīng)器使用技巧匯總
本文將介紹在Spring?Boot應(yīng)用程序中使用過(guò)濾器、攔截器和監(jiān)聽(tīng)器的使用技巧,我們將討論它們之間的區(qū)別,以及何時(shí)使用它們,我們還將提供代碼示例,以幫助您在自己的應(yīng)用程序中使用它們2023-12-12
dubbo服務(wù)引用之創(chuàng)建Invoker流程詳解
這篇文章主要為大家介紹了dubbo服務(wù)引用二之創(chuàng)建Invoker流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
如何用Dos命令運(yùn)行Java版HelloWorld你知道嗎
這篇文章主要介紹了在dos窗口中編譯和運(yùn)行java文件的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08

