【技巧】ScriptEngine--Java动态执行JS Javascript脚本(可调用java的方法) 扩展性很强
JS引擎Nashorn
开发手册
Java Platform, Standard Edition Nashorn User's Guide, Release 14
用户手册
Nashorn User's Guide (oracle.com)
https://docs.oracle.com/en/java/javase/14/nashorn/
甚至可以让前端来写后端业务代码
可以通过js调用java的方法, 通过传参的方式将java对象传给js
可以单独写个JsUtil 让js来调用 里边写常用的方法 统一管理
也可以直接通过Spring去拿到对应的实例
JDK9+ 需要单独依赖
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.6</version>
</dependency>
相关工具类
<!-- hutool工具类-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.20</version>
</dependency>
代码例子
package com.xx.study.Script.Javascript;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.script.ScriptUtil;
import org.junit.jupiter.api.Test;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.HashMap;
import java.util.Map;
public class JavascriptDemo {
private final static String js = """
function test(){
var a = 1;
var b = 3;
return a+b;
}
function 加法(a, b){
return a+b;
}
function MAP传参(map){
return map['a']+map['b'];
}
function 获取json(){
var json = {
最大值:10,
最小值:3,
说明:'测试js方法'
};
return JSON.stringify(json);
}
""";
/**
* 可以考虑结合业务做成动态参数等
* 将js存到数据库进行管理 也可以维护到redis缓存等
* @param args
*/
public static void main(String[] args) {
Object test结果 = ScriptUtil.invoke(js, "test");
System.out.println("test:================================");
//4.0
System.out.println(test结果);
Object 加法结果 = ScriptUtil.invoke(js, "加法", 100, 200);
System.out.println("加法:==============================");
//300.0
System.out.println(加法结果);
Map<String, Integer> MAP参数 = new HashMap<>();
MAP参数.put("a", 500);
MAP参数.put("b", 500);
Object MAP传参结果 = ScriptUtil.invoke(js, "MAP传参", MAP参数);
System.out.println("MAP传参:======================================");
//1000.0
System.out.println(MAP传参结果);
Object 获取json结果 = ScriptUtil.invoke(js, "获取json");
JSONObject entries = JSONUtil.parseObj(获取json结果.toString());
System.out.println("获取json:======================================");
//10
System.out.println(entries.getInt("最大值"));
//测试js方法
System.out.println(entries.getStr("说明"));
//{"最大值":10,"最小值":3,"说明":"测试js方法"}
System.out.println(entries);
}
/**
* ScriptEngine.eval()
*/
@Test
public void jsEngineEvalTest() {
ScriptEngineManager sem = new ScriptEngineManager();
//查找并为给定的扩展创建ScriptEngine。也可用getEngineByName,查找并为给定名称创建ScriptEngine
ScriptEngine jsEngine = sem.getEngineByExtension("js");
try {
jsEngine.eval("var array = [1, 2, 3, 4, 5];for (var i = 0; i < array.length; i++) {print('index:' + i + ',value:' + array[i]);}");
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
/**
* ScriptEngine.eval()
*/
@Test
public void jsEngineEvalTest() {
ScriptEngineManager sem = new ScriptEngineManager();
//查找并为给定的扩展创建ScriptEngine。也可用getEngineByName,查找并为给定名称创建ScriptEngine
ScriptEngine jsEngine = sem.getEngineByExtension("js");
try {
jsEngine.eval("var array = [1, 2, 3, 4, 5];for (var i = 0; i < array.length; i++) {print('index:' + i + ',value:' + array[i]);}");
} catch (ScriptException e) {
e.printStackTrace();
}
}
通过js创建java对象
jjs> var HashMap = Java.type("java.util.HashMap")
jjs> var mapDef = new HashMap()
jjs> var map100 = new HashMap(100)
访问类和实例成员
jjs> Java.type("java.lang.Math").PI
3.141592653589793
jjs> Java.type("java.lang.System").currentTimeMillis()
1375813353330
jjs> Java.type("java.util.Map").Entry
[JavaClass java.util.Map$Entry]
内部类也可以使用内部表示来访问,其中美元符号 () 作为分隔符或点,这与 Java 一致:$
jjs> Java.type("java.util.Map$Entry")
[JavaClass java.util.Map$Entry]
jjs> Java.type("java.util.Map.Entry")
[JavaClass java.util.Map$Entry]
要调用实例方法或访问对象的实例字段,请使用点运算符,类似于在 Java 中完成的方式。下面的示例演示如何在对象上调用该方法:toUpperCase()
String
jjs> var String = Java.type("java.lang.String")
jjs> var str = new String("Hello")
jjs> str
Hello
jjs> var upper = str.toUpperCase()
jjs> upper
HELLO
使用 JavaBeans
Nashorn 使您能够将 JavaBeans 中的访问器和突变器方法视为等效的 JavaScript 属性。属性的名称是不带 or 后缀的 JavaBean 方法的名称,并以小写字母开头。get
set
例如,您可以使用属性调用对象中的 and 方法,如下所示:getYear()
setYear()
java.util.Date
year
jjs> var Date = Java.type("java.util.Date")
jjs> var date = new Date()
jjs> date.year + 1900
2013
jjs> date.year = 2014 - 1900
114
jjs> date.year + 1900
2014
扩展 Java 类
您可以使用将 Java 类型作为第一个参数的函数和将方法实现(以 JavaScript 函数的形式)作为其他参数的函数来扩展类。Java.extend()
以下脚本扩展接口并使用它来构造新对象:java.lang.Runnable
java.lang.Thread
var Run = Java.type("java.lang.Runnable");
var MyRun = Java.extend(Run, {
run: function() {
print("Run in separate thread");
}
});
var Thread = Java.type("java.lang.Thread");
var th = new Thread(new MyRun());
访问 Java 类
使用 Nashorn 访问包和类有两种方法:传统方法是使用全局对象,推荐的方法是使用全局对象。本节介绍这两种方法。Packages
Java
预定义的顶级对象使您能够使用完全限定的名称访问 Java 包和类,就好像它们是对象的属性一样。以下示例演示如何访问包及其类(如果位于类路径中):Packages
Packages
MyPackage
MyClass
MyPackage.jar
jjs> Packages.MyPackage
[JavaPackage MyPackage]
jjs> Packages.MyPackage.MyClass
[JavaClass MyPackage.MyClass]
访问标准 Java 包和类比访问定制包和类更简单。为方便起见,为每个标准 Java 包定义了全局对象:、、、 和 。它们具有与对象的属性相对应的别名。下面的示例演示如何访问包和类:com
edu
java
javax
org
Packages
java.lang
java.lang.System
jjs> java.lang
[JavaPackage java.lang]
jjs> typeof java.lang
object
jjs> java.lang.System
[JavaClass java.lang.System]
jjs> typeof java.lang.System
function
报错
java.lang.NullPointerException: Script for [js] not support !
Cannot invoke "javax.script.ScriptEngine.eval(String)" because "jsEngine" is
项目中使用了 com.github.whvcse包的easy-captcha
验证码依赖,升级至Jdk17后,验证码接口报错:Cannot invoke "javax.script.ScriptEngine.eval(String)" because "engine" is null
,错误原因很明显脚本引擎执行脚本语句报错,因为执行引擎为空。查询相关资料Jdk8自带的JavaScript引擎 nashorn
再升级到Jdk9后就被移除了,从而导致报错
解决办法:添加JavaScript引擎 nashorn
依赖
<dependency>
<groupId>org.openjdk.nashorn</groupId>
<artifactId>nashorn-core</artifactId>
<version>15.4</version>
</dependency>
动态代码 预留js hook文章来源:https://www.toymoban.com/news/detail-788576.html
通过js动态调整业务逻辑 处理数据等 js代码存到数据库 或 缓存文章来源地址https://www.toymoban.com/news/detail-788576.html
到了这里,关于【技巧】ScriptEngine--Java动态执行JS Javascript脚本(可调用java的方法)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!