1. 程式人生 > >JAVA執行javascript方法

JAVA執行javascript方法

之前在一次機緣巧合的情況下,需要時用JAVA執行js方法,查閱了一些文件,找到了相關解決方法,這裡和大家分享一下。
在JDK1.6中為我們提供了一個ScriptEngineManager類,ScriptEngineManager 為 ScriptEngine 類實現一個發現和例項化機制,還維護一個鍵/值對集合來儲存所有 Manager 建立的引擎所共享的狀態。此類使用服務提供者機制列舉所有的 ScriptEngineFactory 實現。
ScriptEngineManager 提供了一個方法,可以返回一個所有工廠實現和基於語言名稱、副檔名和 mime 型別查詢工廠的實用方法所組成的陣列。
鍵/值對的 Bindings(即由管理器維護的 “Global Scope”)對於 ScriptEngineManager 建立的所有 ScriptEngine 例項都是可用的。Bindings 中的值通常公開於所有指令碼中。

通過上面的描述,我們就應該知道了,需要時用到的類為ScriptEngineManagerScriptEngine

我們先來看一下ScriptEngineManager

構造方法如下:

方法名 說明
ScriptEngineManager() 如果呼叫者可訪問執行緒上下文 ClassLoader,則呼叫此構造方法的效果與呼叫 ScriptEngineManager(Thread.currentThread().getContextClassLoader()) 相同
ScriptEngineManager(ClassLoader loader) 此構造方法使用服務呼叫者機制載入對於給定 ClassLoader 可見的 ScriptEngineFactory 實現

方法摘要如下:

返回值 方法名 說明
Object get(String key) 獲取 Global Scope 中指定鍵的值
Bindings getBindings() getBindings
ScriptEngine getEngineByExtension(String extension) 查詢並建立一個給定擴充套件的 ScriptEngine
ScriptEngine getEngineByMimeType(String mimeType) 查詢並建立一個給定 mime 型別的 ScriptEngine
ScriptEngine getEngineByName(String shortName) 查詢並建立一個給定名稱的 ScriptEngine
List<ScriptEngineFactory> getEngineFactories() 返回一個數組,該陣列的元素是發現機制找到的所有 ScriptEngineFactory 類的例項
void put(String key, Object value) 設定 Global Scope 中指定的鍵/值對
void registerEngineExtension(String extension, ScriptEngineFactory factory) 註冊一個 ScriptEngineFactory 來處理擴充套件
void registerEngineMimeType(String type, ScriptEngineFactory factory) 註冊一個 ScriptEngineFactory 來處理 mime 型別。
void registerEngineName(String name, ScriptEngineFactory factory) 註冊一個ScriptEngineFactory 來處理語言名稱
void setBindings(Bindings bindings) setBindings 儲存 globalScope 欄位中的指定 Bindings

我們首先來看一下,JDK為我們提供了哪些可用的指令碼引擎工廠,寫一段程式碼測試一下:

@Test
public void getScriptEngineFactory()
{
   ScriptEngineManager manager = new ScriptEngineManager();
   List<ScriptEngineFactory> factories = manager.getEngineFactories();
   for (ScriptEngineFactory factory : factories)
   {
      System.out.println(factory.getNames());
   }
}

執行結果如下:

[nashorn, Nashorn, js, JS, JavaScript, javascript, ECMAScript, ecmascript]

通過控制檯輸出的結果,我們知道JDK已經為我們實現了js相關的指令碼引擎,我們直接使用就可以了,解決了引擎的問題,下面我們再來看看指令碼引擎如何使用,這就需要ScriptEngine了。

ScriptEngine 是基礎介面,該介面的方法在此規範的每個實現中都必須具有完整的功能。
這些方法提供基本的指令碼功能。為這個簡單介面編寫的應用程式應該對每個實現稍做修改就能夠執行。這包括執行指令碼的方法,以及設定和獲取值的方法。
這些值是兩種型別的鍵/值對。組成第一種型別的鍵/值對中的鍵是此規範或個別實現中保留和定義的鍵。包含保留鍵的鍵/值對中的值具有指定的含義。
另一種型別由那些建立 Java 語言 Bindings 的鍵/值對組成,值通常通過相應鍵或其裝飾的形式用指令碼表示。

方法摘要如下:

返回值 方法名 說明
Bindings createBindings() 返回一個未初始化的 Bindings
Object eval(Reader reader) 除了指令碼的源是以 Reader 形式提供的外,與 eval(String) 相同
Object eval(Reader reader, Bindings n) 除了指令碼的源是以 Reader 形式提供的外,與 eval(String, Bindings) 相同
Object eval(Reader reader, ScriptContext context) 與 eval(String, ScriptContext) 相同,其中指令碼的源是從 Reader 讀取的
Object eval(String script) 執行指定的指令碼
Object eval(String script, Bindings n) 執行指令碼,指令碼執行期間使用 Bindings 引數作為 ScriptEngine 的 ENGINE_SCOPE Bindings
Object eval(String script, ScriptContext context) 立即執行指令碼,該指令碼的源是作為第一個引數傳遞的 String
Object get(String key) 獲取在此引擎的狀態中設定的值
Bindings getBindings(int scope) 返回指定值的範圍
ScriptContext getContext() 返回 ScriptEngine 的預設 ScriptContext,在沒有指定 ScriptContext 時,該 ScriptEngine 的 Bindings、Reader 和 Writer 被用於指令碼執行
ScriptEngineFactory getFactory() 返回此 ScriptEngine 所屬的類的 ScriptEngineFactory
void put(String key, Object value) 設定 ScriptEngine 的狀態中的鍵/值對,它建立一個將在指令碼執行中使用或者以其他方式使用的 Java Language Binding,具體取決於該鍵是否被保留
void setBindings(Bindings bindings, int scope) 設定將由指令碼使用的指定值的範圍。
void setContext(ScriptContext context) 設定 ScriptEngine 的預設 ScriptContext,在沒有指定 ScriptContext 時,該 ScriptEngine 的 Bindings、Reader 和 Writer 被用於指令碼執行

下面我們通過程式碼來演示具體的使用方法

首先我們先來看一下如何執行一段簡單的js表示式:

@Test
public void invokeExpression() throws ScriptException
{
   ScriptEngineManager manager = new ScriptEngineManager();
   ScriptEngine engine = manager.getEngineByName("js");
   String js = "1 + 2";
   Integer result = (Integer) engine.eval(js);
   System.out.println(result);
}

通過這段程式碼,我們很容易的就實現了java執行js的一段表示式。
在java中,我們不僅可以執行js的表示式,我們還可以執行js函式

@Test
public void invokeFunction() throws ScriptException, NoSuchMethodException
{
   ScriptEngineManager manager = new ScriptEngineManager();
   ScriptEngine engine = manager.getEngineByName("js");
   String js = "function welcom(){return 'welcom';}";
   engine.eval(js);
   Invocable invocable = (Invocable) engine;
   String result = (String) invocable.invokeFunction("welcom");
   System.out.println(result);
}

這裡我們用到了Invocable介面,Invocable由 ScriptEngines 實現的可選介面,該 ScriptEngines 的方法允許在以前執行過的指令碼中呼叫程式。當然啦,我們不僅能執行函式,還可以傳遞引數:

@Test
   public void invokeFunctionWithParam() throws ScriptException, NoSuchMethodException
{
   ScriptEngineManager manager = new ScriptEngineManager();
   ScriptEngine engine = manager.getEngineByName("js");
   String js = "function welcom(name){return 'welcom ' + name;}";
   engine.eval(js);
   Invocable invocable = (Invocable) engine;
   String result = (String) invocable.invokeFunction("welcom", "jianggujin");
   System.out.println(result);
}

除此之外,我們還可以將java物件注入到js程式碼中執行:

@Test
public void inject() throws ScriptException, NoSuchMethodException
{
   ScriptEngineManager manager = new ScriptEngineManager();
   ScriptEngine engine = manager.getEngineByName("js");
   Date date = new Date();
   System.out.println(date.getTime());
   engine.put("date", date);
   String js = "function getTime(){return date.getTime();}";
   engine.eval(js);
   Invocable invocable = (Invocable) engine;
   Long result = (Long) invocable.invokeFunction("getTime");
   System.out.println(result);
}

是不是很神奇呢?下面再介紹一種更神奇的,java通過執行緒啟動js函式:

public void runThread() throws ScriptException, NoSuchMethodException
{
   ScriptEngineManager manager = new ScriptEngineManager();
   ScriptEngine engine = manager.getEngineByName("js");
   engine.put("out", System.out);
   String js = "var obj=new Object();obj.run=function(){out.println('thread...')}";
   engine.eval(js);
   Object obj = engine.get("obj");
   Invocable inv = (Invocable) engine;
   Runnable r = inv.getInterface(obj, Runnable.class);
   Thread t = new Thread(r);
   t.start();
}

好了,到這裡java執行js的方法已經介紹的差不多了,下面貼出完整的測試程式碼:

import java.util.Date;
import java.util.List;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

/**
 * 執行javascript
 * 
 * @author jianggujin
 *
 */
public class RunJavascript
{

   public void getScriptEngineFactory()
   {
      ScriptEngineManager manager = new ScriptEngineManager();
      List<ScriptEngineFactory> factories = manager.getEngineFactories();
      for (ScriptEngineFactory factory : factories)
      {
         System.out.println(factory.getNames());
      }
   }

   public void invokeExpression() throws ScriptException
   {
      ScriptEngineManager manager = new ScriptEngineManager();
      ScriptEngine engine = manager.getEngineByName("js");
      String js = "1 + 2";
      Integer result = (Integer) engine.eval(js);
      System.out.println(result);
   }

   public void invokeFunction() throws ScriptException, NoSuchMethodException
   {
      ScriptEngineManager manager = new ScriptEngineManager();
      ScriptEngine engine = manager.getEngineByName("js");
      String js = "function welcom(){return 'welcom';}";
      engine.eval(js);
      Invocable invocable = (Invocable) engine;
      String result = (String) invocable.invokeFunction("welcom");
      System.out.println(result);
   }

   public void invokeFunctionWithParam() throws ScriptException, NoSuchMethodException
   {
      ScriptEngineManager manager = new ScriptEngineManager();
      ScriptEngine engine = manager.getEngineByName("js");
      String js = "function welcom(name){return 'welcom ' + name;}";
      engine.eval(js);
      Invocable invocable = (Invocable) engine;
      String result = (String) invocable.invokeFunction("welcom", "jianggujin");
      System.out.println(result);
   }

   public void inject() throws ScriptException, NoSuchMethodException
   {
      ScriptEngineManager manager = new ScriptEngineManager();
      ScriptEngine engine = manager.getEngineByName("js");
      Date date = new Date();
      System.out.println(date.getTime());
      engine.put("date", date);
      String js = "function getTime(){return date.getTime();}";
      engine.eval(js);
      Invocable invocable = (Invocable) engine;
      Long result = (Long) invocable.invokeFunction("getTime");
      System.out.println(result);
   }

   public void runThread() throws ScriptException, NoSuchMethodException
   {
      ScriptEngineManager manager = new ScriptEngineManager();
      ScriptEngine engine = manager.getEngineByName("js");
      engine.put("out", System.out);
      String js = "var obj=new Object();obj.run=function(){out.println('thread...')}";
      engine.eval(js);
      Object obj = engine.get("obj");
      Invocable inv = (Invocable) engine;
      Runnable r = inv.getInterface(obj, Runnable.class);
      Thread t = new Thread(r);
      t.start();
   }

   public static void main(String[] args) throws NoSuchMethodException, ScriptException
   {
      new RunJavascript().runThread();
   }

}