JAVA中执行JavaScript代码并获取返回值

场景描述

今天在CSDN上偶然看到一个帖子对于一段字符串 “var p=‘xxxx’” 怎么在java里获得p的值,我想起了以前一个很有意思的场景,我的一位很NB的前同事做了一件很了不起的事,他当时配置acitiviti流程引擎的时候为了做变量控制,把变量控制的条件写成了一个javascript的表达式,大概类似于groupNumber==1&&hasRead&&ticketType==1这种表达式,然后再JAVA代码中把这些表达式执行了一下获取一个布尔值作为流程控制的依据,我当时觉得思想很不错!

后来,我在同一个项目中遇到了另外一个场景,在计算一个报表的某个值的时候需要使用对象中的一个参数,这个参数是用户通过页面配置的,大概是这种样子100*23230这种,我在看到了我这位前辈的思想之后,我采用的方式是这样的,把这个字段作为字符串让用户在页面上输入,然后通过前辈的思想把这个字符串作为一个JavaScript代码段执行一下,获取返回值,用这个返回值作为计算参数参与报表计算.

实现思路

把入参作为JavaScript代码,通过JavaScript执行引擎执行这个代码,获取返回值

技术要点

合理的使用JavaScript解析引擎

代码实现

package com.hykj.util;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.Map;
import java.util.Set; /**
* java执行javaScript代码的工具类
*
* @author weizj
*/
public class JavaScriptUtil { /** 单例的JavaScript解析引擎 */
private static ScriptEngine javaScriptEngine; static {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine scriptEngine = manager.getEngineByName("js");
if (scriptEngine == null) {
throw new RuntimeException("获取JavaScript解析引擎失败");
}
javaScriptEngine = scriptEngine;
} /**
* 执行一段JavaScript代码
*
* @param script JavaScript的代码
* @return JavaScript代码运行结果的值
* @throws ScriptException JavaScript代码运行异常
*/
public static Object execute(String script) throws ScriptException {
return javaScriptEngine.eval(script);
} /**
* 运行一个JavaScript代码段,并获取指定变量名的值
*
* @param script 代码段
* @param attributeName 已知的变量名
* @return 指定变量名对应的值
* @throws ScriptException JavaScript代码运行异常
*/
public static Object executeForAttribute(String script, String attributeName) throws ScriptException {
javaScriptEngine.eval(script);
return javaScriptEngine.getContext().getAttribute(attributeName);
} /**
* 获取当前语句运行后第一个有值变量的值
*
* @param script 代码段
* @return 第一个有值变量的值
* @throws ScriptException JavaScript代码运行异常
*/
public static Object executeForFirstAttribute(String script) throws ScriptException { //这里重新获取一个JavaScript解析引擎是为了避免代码中有其他调用工具类的地方的变量干扰
//重新获取后,这个JavaScript解析引擎只执行了这次传入的代码,不会保存其他地方的变量
//全局的解析器中会保存最大200个变量,JavaScript解析引擎本身最大保存100个变量
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine scriptEngine = manager.getEngineByName("js");
if (scriptEngine == null) {
throw new RuntimeException("获取JavaScript解析引擎失败");
} scriptEngine.eval(script);
ScriptContext context = scriptEngine.getContext();
if (context == null) {
return null;
}
Bindings bindings = context.getBindings(ScriptContext.ENGINE_SCOPE);
if (bindings == null) {
return null;
}
Set<Map.Entry<String, Object>> entrySet = bindings.entrySet();
if (entrySet == null || entrySet.isEmpty()) {
return null;
}
for (Map.Entry<String, Object> entry : entrySet) {
if (entry.getValue() != null) {
return entry.getValue();
}
}
return null;
}
}

测试方法

   public static void main(String[] args) throws ScriptException {
Integer testExecute = (Integer) execute("2*3");
String testExecuteForAttribute = (String) executeForAttribute("var value = 'a'+ 'dc'", "value");
Boolean testExecuteForFirstAttribute = (Boolean) executeForFirstAttribute("var a = 6==2*3"); System.out.println(testExecute);
System.out.println(testExecuteForAttribute);
System.out.println(testExecuteForFirstAttribute); System.out.println("test over ...."); }

运行结果

"C:\Program Files\Java\jdk1.8.0_191\bin\java.exe" -agentlib:...
com.hykj.util.JavaScriptUtil
Connected to the target VM, address: '127.0.0.1:56704', transport: 'socket'
6
adc
true
test over ....
Disconnected from the target VM, address: '127.0.0.1:56704', transport: 'socket' Process finished with exit code 0

从运行结果中可以看到,需求是可以实现的,包含了执行代码并获取值

新增的executeForAttribute是为了解答开篇的帖子的问题

后来我又想了一下,可能是运行语句的时候并不知道变量名于是增加了一个不太严谨的executeForFirstAttribute方法用来处理一下这个问题,但是自我感觉这个方法并不很合适

改进空间

  1. 语句中如果包含参数是无法执行的,等有空的时候研究一下如何传参
  2. 目前只试验了Integer,String,Boolean这三个常用的类型,如果是如Person等复杂类型,不知道代码运行情况如何
  3. executeForFirstAttribute方法不知道如何改进才能满足大多数情况

JAVA中执行JavaScript代码并获取返回值的更多相关文章

  1. 尚学堂 215 在java中执行JavaScript代码

    package com.bjsxt.test; import java.io.FileReader; import java.net.URL; import java.util.List; impor ...

  2. UWP 在 WebView 中执行 JavaScript 代码(用于模拟用户输入等) - walterlv

    原文:UWP 在 WebView 中执行 JavaScript 代码(用于模拟用户输入等) - walterlv UWP 在 WebView 中执行 JavaScript 代码(用于模拟用户输入等) ...

  3. Java中运行javascript代码

    Java中运行javascript代码 1.Java 代码 2.JS代码 2.1demoWithParams.js 2.2demoWithListParams.js 原文作者:russle 原文地址: ...

  4. python中执行javascript代码

    python中执行javascript代码: 1.安装相应的库,我使用的是PyV8 2.import PyV8 ctxt = PyV8.JSContext()     ctxt.enter()     ...

  5. java中执行js代码

    要在java中执行js代码,首先明白,java不支持浏览器本身的方法.支持自定义的js方法,否则会报错 先新建一个js文件:jsss.js 内容如下: function aa(a,b){ return ...

  6. 在Java中执行js代码

    在某些特定场景下,我们需要用Java来执行Js代码(如模拟登录时,密码被JS加密了的情况),操作如下: ScriptEngineManager mgr = new ScriptEngineManage ...

  7. UWP 在 WebView 中执行 JavaScript 代码(用于模拟用户输入等)

    UWP 中使用 WebView 时可以在网页中额外执行一些代码.于是你几乎可以在网页上做任何事情,那些你可以在浏览器控制台中做的事情. 本文将介绍做法. 本文内容 准备环境 执行 JavaScript ...

  8. java中执行javascript案例

    Nashorn js engine官方文档 https://docs.oracle.com/javase/7/docs/technotes/guides/scripting/programmer_gu ...

  9. python 利用python的subprocess模块执行外部命令,获取返回值

    有时执行dos命令需要保存返回值 需要导入库subprocess import subprocess p = subprocess.Popen('ping www.baidu.com', shell= ...

随机推荐

  1. chrome浏览器和其它浏览器对scrollTop、scrollLeft的获取方法

    chrome浏览器不能通过document.documentElement.scrollTop来获得距上滚动距离,得到的值始终为0:需要用document.body.scrollTop来获取.其它浏览 ...

  2. python中os模块操作目录与文件名小结

    (1). 创建目录: SigleDir = 'sigle_layer' MultiDir = 'D:\\Web\\multi_layer' 创建单层目录: os.mkdir(SigleDir) 创建多 ...

  3. python的apidoc使用

    一.apidoc的安装 npm install apidoc -g -g参数表示全局安装,这样在哪儿都能使用. 二.apidoc在python接口代码中的使用 def index(): "& ...

  4. LVM XFS增加硬盘分区容量最后一步的时候动态扩容报错

    在我们lvextend扩容完之后,想动态扩容时出现错误.我们可以用以下命令来进行操作. 若不是xfs我们可以用resize2fs,这里报错了 [root@Mysql01-213-66 ~]# resi ...

  5. Django SCRF跨站点请求伪造

    使用Django发POSTt请求的时候经常会遇到Forbidden的错误,然后直接了当的方法就是去setting里面吧csrf中间件注释掉,其实csrf是django给我们提供的防护措施. CSRF就 ...

  6. Presto集群部署和配置

    参考文档:1.https://blog.csdn.net/zzq900503/article/details/79403949 prosto部署与连接hive使用                 2. ...

  7. Java框架spring Boot学习笔记(九):一个简单的RESTful API

    RESTful API设计需求如下: User.java package com.springboot.test; public class User { private Long id; priva ...

  8. HC-05蓝牙模块配对步骤

    参考:https://blog.csdn.net/m0_37182543/article/details/76383247

  9. [Solution] 969. Pancake Sorting

    Difficulty: Medium Problem Given an array A, we can perform a pancake flip: We choose some positive ...

  10. 【SVN】关于提交代码时的问题

    将项目上传到SVN弹出框提示One added/edited TODO item was found. Would you like to review it? 然后百度说这样解决: 造成这个原因是因 ...