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();
   }

}
目录
相关文章
|
8天前
|
Java API
Java 对象释放与 finalize 方法
关于 Java 对象释放的疑惑解答,以及 finalize 方法的相关知识。
33 17
|
2天前
|
Java 测试技术 Maven
Java一分钟之-PowerMock:静态方法与私有方法测试
通过本文的详细介绍,您可以使用PowerMock轻松地测试Java代码中的静态方法和私有方法。PowerMock通过扩展Mockito,提供了强大的功能,帮助开发者在复杂的测试场景中保持高效和准确的单元测试。希望本文对您的Java单元测试有所帮助。
8 2
|
4天前
|
JavaScript 前端开发 Go
异步加载 JS 的方法
【10月更文挑战第24天】异步加载 JavaScript 是提高网页性能和用户体验的重要手段。通过使用不同的方法和技术,可以实现灵活、高效的异步加载 JavaScript。在实际应用中,需要根据具体情况选择合适的方法,并注意处理可能出现的问题,以确保网页能够正常加载和执行。
|
9天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
11 3
|
9天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
10 2
|
9天前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
11 1
|
9天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
20 1
|
9天前
|
Java
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅
在Java多线程编程中,`wait()`和`notify()`方法的相遇如同一场奇妙的邂逅。它们用于线程间通信,使线程能够协作完成任务。通过这些方法,生产者和消费者线程可以高效地管理共享资源,确保程序的有序运行。正确使用这些方法需要遵循同步规则,避免虚假唤醒等问题。示例代码展示了如何在生产者-消费者模型中使用`wait()`和`notify()`。
15 1
|
9天前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
21 1
|
9天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
17 1