欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java中运行js代码

程序员文章站 2022-06-07 13:22:35
...

项目需求:

客户动态填写一些规则,根据规则生成告警提醒变化之类的信息,例如温度过高给出告警,设备属性变化给出提示等等。然而客户的判断这些变化的规则是不断改变的,因此需要动态输入。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, 之后的是nashorn

官方文档:
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= "JavaScript";
    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

本文转自:russle
https://blog.csdn.net/russle/article/details/85214044?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-2.channel_param