之前在一次机缘巧合的情况下,需要时用JAVA执行js方法,查阅了一些文档,找到了相关解决方法,这里和大家分享一下。
在JDK1.6中为我们提供了一个ScriptEngineManager
类,ScriptEngineManager 为 ScriptEngine 类实现一个发现和实例化机制,还维护一个键/值对集合来存储所有 Manager 创建的引擎所共享的状态。此类使用服务提供者机制枚举所有的 ScriptEngineFactory 实现。
ScriptEngineManager 提供了一个方法,可以返回一个所有工厂实现和基于语言名称、文件扩展名和 mime 类型查找工厂的实用方法所组成的数组。
键/值对的 Bindings(即由管理器维护的 “Global Scope”)对于 ScriptEngineManager 创建的所有 ScriptEngine 实例都是可用的。Bindings 中的值通常公开于所有脚本中。
通过上面的描述,我们就应该知道了,需要时用到的类为ScriptEngineManager
和ScriptEngine
。
我们先来看一下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();
}
}