Rhino
一、简介
Rhino是一种用Java编写的开源JavaScript解释器,目的是用Java API实现轻松编写JavaScript程序。Rhino能自动完成Java原生类型与JavaScript原生类型之间的相互转换。
二、安装与运行
-
Rhino可以做为独立的解释程序在命令行运行:
java -jar rhino-1.7.7.2.jar
三、语法
- Rhino定义了少量的全局函数:
//全局输出函数,将内容输出到控制台
print(x);
//使用JS 1.7语言特性
version(170);
//加载并执行1个或多个JS文件
load(filename, ...);
//读取文本文件内容,并以字符串形式返回
readFile(file);
//读取URL地址的内容,并以字符串形式返回
readUrl(url);
//使用命令行参数运行系统命令
runCommand(cmd, [args...]);
//退出Rhino
quit();
- Rhino可以将Java包和类表示成JS对象:
//全局变量Packages是Java包层次结构的根
Packages.any.package.name
//Packages.java可以简写为java
java.lang
//Packages.javax可以简写为javax
javax.swing
//Rhino把Java包的类表示为JS对象,可以通过赋值给变量获得对应的短名
var System = java.lang.System;
var JFrame = javax.swing.JFrame;
var HashMap = java.util.HashMap;
//或者用更正式的方式导入
importClass(java.util.HashMap);
//可以使用importPackage()导入包。注:不要导入java.lang,因为有太多的名称和JS全局变量冲突
importPackage(java.util);
//可以使用JavaImporter引入多个包中的类,并在with语句中使用它返回的对象
var pkgs = JavaImporter(Packages.java.util, java.swing);//Packages.java中的Packages可省略
with(pkgs){
//在此处可以使用List、JFrame等类
}
- new和instanceof关键字:
//可以使用new关键字实例化Java类
var list = new java.util.ArrayList();
//Rhino会执行隐式转换,下面的例子中会将'file.txt'自动转为java.lang.String类型
var file = new java.io.File('file.txt');
//调用Java对象file的length方法
var len = file.length();
//rhino能让JS的instanceof运算符用于Java对象
print(list instanceof java.util.List);
- Rhino也允许JS查询或设置Java类的静态字段和Java对象的实例字段。当Java对象存在setter/getter方法时,Rhino会将其显示为Js对象的属性。
var File = java.io.File;
var f = new File('file.txt');
print(f.name);//调用java.io.File.getName()方法
- 重载函数调用:一般情况下,Rhino可以根据调用方法时传递的参数类型判断出要调用的方法,有时也需要通过名字和签名来明确识别方法。
说明:按官方文档,加载自定义扩展类时只要保证.jar
或.class
文件在classpath路径下即可,但我在做测试时这种方式并不可行,需要在命令行下运行以下命令才可以(rhino-test.jar为测试使用类所在jar文件):
java -classpath .\rhino-1.7.7.2.jar;.\rhino-test.jar org.mozilla.javascript.tools.shell.Main
package com.model;
/**
* 测试账户类
*/
public class Account {
private float money;
public void saveMoney(int amount){
money += amount;
System.out.println("调用int类型参数的方法");
}
public void saveMoney(float amount){
money += amount;
System.out.println("调用float类型参数的方法");
}
public float getMoney() {
return money;
}
public void setMoney(float money) {
this.money = money;
}
}
//rhino脚本
//引入用户自定义包时Packages不能省略
var account = new Packages.com.model.Account();
//调用int参数方法
account['saveMoney(int)'](50);
//调用float参数方法
account['saveMoney(float)'](55.55);
- 可以使用for/in循环遍历Java类和对象的方法和属性:
importClass(java.lang.System);
importPackage(Packages.com.model);
//输出java.lang.System类的静态成员
for(var f in System){
print(f);
}
//输出com.model.Account类的实例成员
var account = new Account();
for(var p in account){
print(p);
}
- Rhino允许像JS数组那样获取、设置Java数组元素:
//必须使用java.lang.reflect.Array来实现数组的创建
var words = java.lang.reflect.Array.newInstance(java.lang.String, 10);
var arr = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'];
//创建完成后,就可以像JS数组一样使用它们
for(var i = 0; i < words.length; i++){
words[i] = arr[i];
}
- 实现接口或扩展抽象类
//接口
package com.api;
import java.util.List;
import com.model.Account;
public interface IAccountService {
public Account queryById(int id);
public List<Account> queryByName(String name);
}
//抽象类
package com.api;
import com.model.Account;
public abstract class AbstractAccountHelper {
public abstract void analysis(Account account);
}
//实现接口
importPackage(Packages.com.api);
var service = new IAccountService({
queryById: function(id){
print('query by id: ' + id);
},
queryByName: function(name){
print('query by name: ' + name);
}
});
//扩展抽象类
var service = new AbstractAccountHelper({
analysis: function(account){
print('analysis account');
}
});
//如果一个对象需要实现多重接口,需要使用JavaAdapter
var obj = new JavaAdapter(java.lang.Runnable, AbstractAccountHelper, {
run: function(){},
analysis: function(){}
});
//当接口或抽象类的方法只有一个时,可以直接使用一个函数即可
button.addActionListener(function(e){
print('button clicked');
});
- 当Java方法抛出异常时,Rhino将其作为JS异常传递,可以通过JS的Error对象的javaException属性获取原始的Java Exception:
try{
java.lang.System.getProperty(null);
}catch(e){
print(e.javaException);
}
- Rhino会按需要自动转换原始数字、布尔值和null。因为JavaScript没有char类型,因此Java的char类型会被当作JavaScript数字对待。 JavaScript字符串能自动转换为Java字符串,但java.lang.String对象不能转换为JavaScript字符串。
var version = java.lang.System.getProperty('java.version');
typeof version // => object
version.length() // => 8
//为了将Java字符串转换为JavaScript字符串,需要使用JavaScript的String()方法:
var version = String(java.lang.System.getProperty('java.version'));
- Java代码中执行Javascript代码
package com.rhino;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
public class RunScript {
public static void main(String[] args) {
Context ctx = Context.enter();
try{
Scriptable scope = ctx.initStandardObjects();
String source = "var d = new Date(); d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate();";
Object result = ctx.evaluateString(scope, source, "<cmd>", 1, null);
System.out.println(Context.toString(result));
}finally{
Context.exit();
}
}
}
运行结果:2018-1-4
参考资料
- Javascript权威指南(第6版)
- Rhino GitHub地址
- Rhino Documentation