项目需求:
客户动态填写一些规则,根据规则生成告警提醒变化之类的信息,例如温度过高给出告警,设备属性变化给出提示等等。然而客户的判断这些变化的规则是不断改变的,因此需要动态输入。jvm支持多种动态语言,我们可以从java中调用JavaScript、Groovy、Ruby以及Scheme和Haskell编写的脚本,考虑到语言的流行程度,我们选择了js。
实例代码:
本代码主要来自《写给大忙人看的Jave SE 9核心技术》一书第14章,编译与脚本。
首先,我们需要获取脚本执行引擎。脚本执行引擎就是以特定语言执行脚本的类库。jvm启动是,就会启动这些脚本引擎,我们可以通过new ScriptEngineManager()来获取脚本引擎管理器,然后通过getEngineByName(...)方法获取的对应的脚本引擎,我们使用的JavaScript语言,因此使用参数“nashorn”获取ScriptEngine。
注意:There is no requirement for a given Java Virtual Machine (JVM) to include any engines by default, but the Oracle JVM (Java 6 and later) includes a JavaScript engine, based on Rhino version 1.6R2 before Java 8, and Nashorn since Java 8.
Java 8 之前的jvm中JavaScript的ScriptEngine name Rhino, 之后的是Rashorn
官方文档:
https://jcp.org/aboutJava/communityprocess/final/jsr223/index.html Scripting for the JavaTM Platform
package com.yq.js;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.experimental.var;
import lombok.extern.slf4j.Slf4j;
import lombok.extern.java.Log;
import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.File;
import java.io.Reader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Simple to Introduction
* className: JavaJSDemo
*
* @author EricYang
* @version 2018/12/22 9:51
*/
@Slf4j
public class JavaJSDemo {
private static final String JS_ENGINE_NAME= "nashorn";
private final ScriptEngineManager sem = new ScriptEngineManager();
private final ScriptEngine engine = sem.getEngineByName(JS_ENGINE_NAME);
public static void main(String[] args) {
JavaJSDemo demo = new JavaJSDemo();
demo.bindingDemo();
demo.reDirectIODemo();
demo.invokeFunctionDemo();
demo.invokeFunctionByFileDemo();
}
public void bindingDemo() {
try {
engine.put("msg", "hello world!");
Object result = engine.eval("msg");
log.info("result=" + result);
engine.put("k", 20);
result = engine.eval("k + 1");
log.info("result=" + result);
result = engine.eval("n = 1738");
log.info("result=" + result);
result = engine.get("n");
log.info("result=" + result);
Bindings scope = engine.createBindings();
scope.put("key", "西安");
result = engine.eval("key + '市'", scope);
log.info("result=" + result);
}
catch (ScriptException se) {
log.warn("binding demo exception.", se);
}
}
public void reDirectIODemo() {
log.info("--- redirect IO ---" );
try {
StringWriter writer = new StringWriter();
engine.getContext().setWriter(writer);
engine.put("msg", "hello world!");
//任何一print函数输出的内容都会送到writer对象中
Object result = engine.eval("print(msg)");
log.info("result=" + result);
log.info("result=" + writer.toString());
//js中直接调用java的System.out.println
result = engine.eval("java.lang.System.out.println(msg)");
log.info("result=" + result);
log.info("result=" + writer.toString());
}
catch (ScriptException se) {
log.warn("binding demo exception.", se);
}
}
public boolean invokeFunctionDemo() {
log.info("--- invokeFunction ---" );
boolean result = true;
try {
engine.put("msg", "hello world!");
String str = "var user = {name:'张三',age:18,city:['陕西','台湾']};";
engine.eval(str);
log.info("Get msg={}", engine.get("msg"));
//获取变量
engine.eval("var sum = eval('1 + 2 + 3*4');");
//调用js的eval的方法完成运算
log.info("get sum={}", engine.get("sum"));
JSONObject msg = new JSONObject();
msg.put("temperature", 125);
msg.put("humidity", 20);
msg.put("voltage", 220);
msg.put("electricity", 13);
JSONObject metadata = new JSONObject();
metadata.put("deviceName", "空气质量检测器01");
metadata.put("contacts", "张三");
JSONObject msgType = new JSONObject();
//msgType.put("type", "deviceTelemetryData");
msgType.put("type", "deviceTelemetryData1");
//定义函数
String func = "var result = true; \r\n" +
"if (msgType.type = 'deviceTelemetryData') { \r\n" +
" if (msg.temperature >0 && msg.temperature < 33) { \n result = true ;} \n" +
" else { \n result = false;} \n" +
"} else { \n result = false; \n" +
" var errorMsg = msgType.type + ' is not deviceTelemetryData'; \n" +
" print(msgType.type) \n } \n\n" +
"return result";
log.info("func = {}", func);
engine.eval("function filter(msg, metadata, msgType){ " + func + "}");
// 执行js函数
Invocable jsInvoke = (Invocable) engine;
Object obj = jsInvoke.invokeFunction("filter", msg, metadata, msgType);
//方法的名字,参数
log.info("function result={}", obj);
result = (Boolean)obj;
}
catch(Exception ex) {
log.warn("exception", ex);
result = false;
}
return result;
}
public int invokeFunctionByFileDemo() {
log.info("--- invokeFunction ---" );
int result = 0;
try {
log.info("Current dir={}", System.getProperty("user.dir"));
//\AkkaDemo\src\main\resources\demo.js
File file = new File("./AkkaDemo/src/main/resources/demo.js");
Reader reader = Files.newBufferedReader(file.toPath(), Charset.defaultCharset());
engine.put("user", "{name:'张三',age:18,city:['陕西','台湾']};");
Object obj = engine.eval(reader);
log.info("get age={}", engine.get("age"));
log.info("function result={}", obj);
URL resource = this.getClass().getClassLoader().getResource("demo2.js");
FileReader fileReader = new FileReader(resource.getPath());
engine.eval(fileReader);
//执行js函数
Invocable jsInvoke = (Invocable)engine;
obj = jsInvoke.invokeFunction("myAdd", 1, 2);
log.info("myAdd obj={}", obj);
}
catch(Exception ex) {
log.warn("exception", ex);
}
return result;
}
}
说明:
1,demo.js 内容为 var age = 20;
2,demo2.js 内容为
function myAdd(a,b){
var sum = a + b;
return sum;
}
运行效果:
11:34:26.170 [main] INFO com.yq.js.JavaJSDemo - result=hello world!
11:34:26.186 [main] INFO com.yq.js.JavaJSDemo - result=21.0
11:34:26.195 [main] INFO com.yq.js.JavaJSDemo - result=1738
11:34:26.195 [main] INFO com.yq.js.JavaJSDemo - result=1738
11:34:26.205 [main] INFO com.yq.js.JavaJSDemo - result=西安市
11:34:26.205 [main] INFO com.yq.js.JavaJSDemo - --- redirect IO ---
11:34:26.212 [main] INFO com.yq.js.JavaJSDemo - result=null
11:34:26.213 [main] INFO com.yq.js.JavaJSDemo - result=hello world!
hello world!
11:34:26.253 [main] INFO com.yq.js.JavaJSDemo - result=null
11:34:26.253 [main] INFO com.yq.js.JavaJSDemo - result=hello world!
11:34:26.253 [main] INFO com.yq.js.JavaJSDemo - --- invokeFunction ---
11:34:26.261 [main] INFO com.yq.js.JavaJSDemo - Get msg=hello world!
11:34:26.272 [main] INFO com.yq.js.JavaJSDemo - get sum=15
11:34:26.280 [main] INFO com.yq.js.JavaJSDemo - func = var result = true;
if (msgType.type = 'deviceTelemetryData') {
if (msg.temperature >0 && msg.temperature < 33) {
result = true ;}
else {
result = false;}
} else {
result = false;
var errorMsg = msgType.type + ' is not deviceTelemetryData';
print(msgType.type)
}
return result
11:34:26.317 [main] INFO com.yq.js.JavaJSDemo - function result=false
11:34:26.317 [main] INFO com.yq.js.JavaJSDemo - --- invokeFunction ---
11:34:26.317 [main] INFO com.yq.js.JavaJSDemo - Current dir=D:\E\workspaceGitub\springboot
11:34:26.328 [main] INFO com.yq.js.JavaJSDemo - get age=20
11:34:26.328 [main] INFO com.yq.js.JavaJSDemo - function result=null
11:34:26.334 [main] INFO com.yq.js.JavaJSDemo - myAdd obj=3.0
Process finished with exit code 0
网友评论