一、需求

之前在做物联网项目时,客户要求在规则引擎中可以编写JS脚本去定义设备的运行和计费逻辑。那么在JS脚本中必然需要调用一些JAVA中的业务功能。所以去看了一下Java中操作JS脚本的内容学习,顺便整理下两者之间各种调用的形式。

二、实现方式

JAVA中可以通过ScriptEngineManager类获取JS脚本引擎的对象

ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");

2.1 简单执行JS脚本

通过脚本引擎对象的eval方法,可以执行简单的表达式获取返回值

public static final String SIMPLE_JS_SCR = "1+1";
String retuerVal = jse.eval(SIMPLE_JS_SCR).toString();

在实际项目中,脚本的需求远不止调用简单表达式这样容易,会有更多复杂需求。集成一些工具类和方法,及一些系统中的业务能力调用,提供给用户使用,让用户基于此自行编写JS脚本达到更加灵活的应用。这样我们通常就会涉及到JS脚本调用JAVA方法,JS中创建JAVA中的对象

2.2 JS中获取JAVA类型,创建JAVA对象

在JS脚本中通过以下代码可以加载到JAVA中的类信息
先定义一个JAVA类

public class ClzInJava {
        private String name;
        private Integer age;

        public ClzInJava(String name, Integer age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "ClzInJava [name=" + name + ", age=" + age + "]";
        }

    }

JS脚本内容

var ClzInJava = Java.type('com.example.ClzInJava');
var obj = new ClzInJava('joe',22);
// 这里不要使用console去打印,console在脚本中是未定义的
print(obj);

通过jse.eval()执行上面的代码就可以看到打印内容 ClzInJava [name=joe, age=22]

2.3 JS中调用JAVA中的方法

JS中调用已经写好的JAVA方法,一是可以通过上面的形式构建对象调用里面的方法或调用类静态方法,另外就是通过给JS脚本注入在JAVA中构建好的对象。这样我们就可以通过把spring的对象注入,实现脚本中调用业务功能,或者单独写一个spring对象用于提供业务方法,以免暴露太多功能给用户。


public class BindObj{
        public String format(String addr){
            return "您的地址是:"+addr;
        }
}

BindObj bindObj = new BindObj();
jse.put("bindObj",bindObj);
//jse.eval() 执行下面的JS脚本会输出 您的地址是:广州市XX路

JS脚本,通过this 获取注入的对象并调用方法

var addr = this.bindObj.format('广州市XX路')
print(addr)

2.4 在java中调用JS脚本中的方法

脚本中的代码如果是做一些转换功能,那么通常情况下我们就会在JAVA中调用脚本的方法用于获取返回值。可以通过将ScriptEngine 转换为InVocable接口来操作调用方法

// jsSource对应下面的脚本内容
jse.eval(jsSource)
Invocable invc = (Invocable) jse;
// 执行方法,传入方法名和参数即可调用
Double ret = (Double) invc.invokeFunction("plus", 1,3);
System.out.println(ret);

JS脚本

function plus(x,y){
	return x+y
}

function mul(x,y){
	return x * y
}

当然如果JS中方法较多,并且为了不每次去写方法名(容易出错),也可以通过getInterface 将JS脚本转换为接口,通过接口来调用

//定义接口,方法签名要和JS中的对应
public interface InnerJsTest {

        Double plus(Integer x, Integer y);

        Double mul(Integer x, Integer y);
}
//转换接口
InnerJsTest in = invc.getInterface(InnerJsTest.class);
//接口调用
Double ret = in.plus(4, 5);
var code = "049d1185-872d-4884-9833-7bc08ab92780"

如果是打包docker镜像,要注意基础镜像的JDK版本,要使用包含了脚本引擎的。不然获取的ScriptEngine对象为空

posted on 2024-03-26 15:11  猿来就是尔  阅读(0)  评论(0)    收藏  举报  来源