轉載來源
這個包用來和JavaScript進行互操作,比如JAVA類可以調用JavaScript中的方法,而JS也可調用 JAVA中的方法.
1、可用的腳本引擎
Java 6提供對執行腳本語言的支持,這個支持來自於JSR223規範,對應的包是javax.script
預設情況下,Java 6只支持javascript腳本,它底層的實現是Mozilla Rhino,它是個純Java的javascript實現
可以通過下面的代碼列出當前環境中支持的腳本引擎:
// 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('Welocme to java world')");
System.out.println();
ScriptEngineManager manager = new ScriptEngineManager();
List<ScriptEngineFactory> factories = manager.getEngineFactories();
for (ScriptEngineFactory f : factories) {
System.out.println("egine name:" + f.getEngineName() + "\n"
+ "engine version:" + f.getEngineVersion() + "\n"
+ "language name:" + f.getLanguageName());
}
結果輸出:
Welocme to java world
egine name:Mozilla Rhino
engine version:1.6 release 2
language name:ECMAScript
可以看到,Java內置只支持javascript一種腳本
但是,只要遵循 JSR223,便可以擴展支持多種腳本語言
可以從https://scripting.dev.java.net/上查找當前已被支持的腳本的第三方庫
2、hello script
示例如上面的代碼所示。
使用的API還是很簡單的,ScriptEngineManager是ScriptEngine的工廠,實例化該工廠的時候會加載可用的所有腳本引擎
從工廠中創建ScriptEngine可以使用getEngineByName、getEngineByExtension或 getEngineByMimeType來得到
只要參數名字能對上。執行腳本調用eval方法即可(效果等同於javascript中的eval)
3、傳遞變量
可以向腳本中傳遞變量,使得Java代碼可以和腳本代碼交互,示例如下:
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.put("a", 4);
engine.put("b", 6);
try {
Object maxNum = engine
.eval("function max_num(a,b){return (a>b)?a:b;}max_num(a,b);");
System.out.println("max_num:" + maxNum);
} catch (Exception e) {
e.printStackTrace();
}
輸出內容:max_num:6
對於上面put的變量,它作用於自身engine範圍內,也就是ScriptContext.ENGINE_SCOPE
put 的變量放到一個叫Bindings的Map中,可以通過 engine.getBindings(ScriptContext.ENGINE_SCOPE).get(「a」);得到put的內容
和 ENGINE_SCOPE相對,還有個ScriptContext.GLOBAL_SCOPE 作用域
其作用的變量是由同一ScriptEngineFactory創建的所有ScriptEngine共享的全局作用域。
4、動態調用
上面的例子中定義了一個javascript函數max_num,可以通過Invocable接口來多次調用腳本庫中的函數
Invocable接口是 ScriptEngine可選實現的接口。下面是個使用示例:
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
try {
engine.eval("function max_num(a,b){return (a>b)?a:b;}");
Invocable invoke = (Invocable) engine;
Object maxNum = invoke.invokeFunction("max_num", 4, 6);
System.out.println(maxNum);
maxNum = invoke.invokeFunction("max_num", 7, 6);
System.out.println(maxNum);
} catch (Exception e) {
// TODO: handle exception
}
上面的invokeFunction,第一個參數調用的腳本函數名,後面跟的可變參數是對應的腳本函數參數
Invocable還有個很酷的功能,就是動態實現接口,它可以從腳本引擎中得到Java Interface 的實例
也就是說,可以定義個一個Java接口,其實現是由腳本完成
以上面的例子為例,定義接口JSLib,該接口中的函數和javascript中的函數簽名保持一致:
public interface JSLib {
public int max_num(int a, int b);
}
[java] view plaincopyprint?
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
try {
engine.eval("function max_num(a,b){return (a>b)?a:b;}");
Invocable invoke = (Invocable) engine;
JSLib jslib = invoke.getInterface(JSLib.class);
int maxNum = jslib.max_num(4, 6);
System.out.println(maxNum);
} catch (Exception e) {
// TODO: handle exception
}
5、使用Java對象
可以在javascript中使用Java代碼,這確實是很酷的事情
在Rhino中,可以通過importClass導入一個類,也可以通過importPackage導入一個包,也可以直接使用全路經的類
在創建對象時,new也不是必須的。示例代碼如下:
try {
String script = "var list = java.util.ArrayList();list.add(\"kafka0102\");print(list.get(0));";
engine.eval(script);
} catch (Exception e) {
e.printStackTrace();
}
又例如:
try {
engine.eval("importPackage(javax.swing);" + "var optionPane = "
+ " JOptionPane.showMessageDialog(null, 'Hello, world!');");
} catch (ScriptException ex) {
ex.printStackTrace();
}
6、編譯執行
腳本引擎默認是解釋執行的,如果需要反覆執行腳本,可以使用它的可選接口Compilable來編譯執行腳本,以獲得更好的性能,示例代碼如下:
try {
Compilable compEngine = (Compilable) engine;
CompiledScript script = compEngine
.compile("function max_num(a,b){return (a>b)?a:b;}");
script.eval();
Invocable invoke = (Invocable) engine;
Object maxNum = invoke.invokeFunction("max_num", 4, 6);
System.out.println(maxNum);
} catch (Exception e) {
e.printStackTrace();
}
7、總結
除了上面提到的特性,腳本引擎還有一些不錯的功能
比如可以執行腳本文件,可以由多線程異步執行腳本等功能
引入腳本引擎,可以對一些配置擴展和業務規則做更強大而靈活的支持,也方便使用者選擇自己熟悉的腳本語言來編寫業務規則等
public class JSTest {
public static void main(String[] args) throws Exception {
// 根據js的後綴名生成一個解析JS的腳本解析引擎
ScriptEngine engin = new ScriptEngineManager()
.getEngineByExtension("js");
// 查詢一下這個引擎是否實現了下面很實用的接口
System.out.println(engin instanceof Invocable);
// 聲明兩個對象,傳入到JS裡面去
JFrame jf = new JFrame("test");
List list = new ArrayList();
// 得到綁定的鍵值對象,把當前的兩個JAVA對象放進去
Bindings bind = engin.createBindings();
bind.put("jf", jf);
bind.put("list", list);
// 把綁定下的鍵值對象放進去,作用域是當前引擎的範圍
engin.setBindings(bind, ScriptContext.ENGINE_SCOPE);
// 用引擎執行一段寫在JS文件裡面的代碼
Object obj = engin.eval(new FileReader("test.js"));
// 這個時候返回值當然 是null了
System.out.println(obj);
// 把當前的引擎強制轉為Invocable,這樣就可以調用定義在JS文件裡面的一個一個函數了
Invocable in = (Invocable) engin;
// 得到了從JS裡面返回來的對象
List l = (List) in.invokeFunction("getNames");
System.out.println(l);
// 調用一下定義在JS裡面的另一個函數
in.invokeFunction("testJS");
// 最後調用一個函數,該函數可以使我們前面定義的窗體顯示出來
in.invokeFunction("doSth");
}
}
下面是定義在test.js裡面的內容
[javascript] view plaincopyprint?
function doSth()
{
jf.setSize(500,300);
jf.setVisible(true);
jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);
}
function getNames()
{
list.add("doSth");
list.add("getNames");
return list;
}
function testJS()
{
print('Hello world!');
}
我們可以看到,在JAVA運行了以後,窗體會顯示出來,並且我們可以接收到從JS解析引擎裡面傳回的數據
當然我們也可以調用一個很普通的JS函數
想像一下,如果我們把我們程序運行時的一些對象都設到Bindings裡面去,那麼我們JS豈不是有很大的自由度了嗎?
因為JS裡面也可以操作我們的Java對象了,並且我們可以像ava編程一樣的對JS編程了,還不用再編譯,馬上就可以運行.靈活性豈不是變得更高了嗎?
參考自:
【1】javax.script包探秘 javascript與java交互執行
http://blog.csdn.net/lvshow/article/details/7252236
【2】Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/
沒有留言:
張貼留言