JAVA运行Script文件
前言
在现代软件开发中,JavaScript不仅仅限于浏览器端的使用,Node.js的出现让JavaScript得以在服务器端大展拳脚。然而,当需要在Java应用程序中运行JavaScript代码时,我面临了一系列挑战。尽管Java提供了多种执行脚本的方法,如Nashorn引擎直接执行JavaScript代码,但随着Java版本的更新Nashorn引擎最终被弃用,迫使我们寻找替代方案。此外,其他一些方法,如使用Rhino引擎或GraalVM,虽然可行,但可能因为兼容性、性能或项目特定需求而受限。
在尝试了多种方法后,我们发现直接使用Java调用外部Node.js进程来运行JavaScript脚本是一种稳定且兼容性好的方法。这种方法虽然牺牲了一些性能,但提供了更强的灵活性和对Node.js生态系统的全面支持。在本教程中,我们将探讨如何通过Java代码启动Node.js进程并执行JavaScript脚本,同时读取执行结果和错误日志。
说明: 使用java进行script脚本,我所遇到的问题,希望读者朋友,能有效的规避,真是太痛苦了
-
Nashorn引擎 : 使用这个引擎,进行运行js,如果你的java版本的低于11,那么可以直接的进行导入进行使用,否则需要进行引入对应的依赖才可以进行使用,但是目前这个引擎,对于js脚本中进行引入导包(require)语句,并不支持,所以如果你的需要进行使用js的第三包工具,那么不建议你进行使用(我是通过hutool的封装进行运行测试)。
<dependency> <groupId>org.openjdk.nashorn</groupId> <artifactId>nashorn-core</artifactId> <version>15.4</version> </dependency>
-
我也进行切换了GraalVM版本的java,但是没有成功。希望有简单好的的方法的大佬们能在评论区进行提供,说明,本人感激不尽!
提示 : 如果你只是想要实现java进行执行js程序,在文本的最后也提供了进行书写好的工具类,欢迎使用!
1. 环境准备
- 确保您的系统中已经安装了Node.js。您可以通过在命令行中运行
node -v
来检查Node.js是否已安装以及其版本。 - 如果你要进行运行python等脚本,请保证你的系统中存在这个程序。
- JAVA 17
2. 创建JavaScript脚本
首先,创建一个简单的JavaScript文件,比如 sum.js
:
复制// sum.js
function sum(a, b) {
return a + b;
}
console.log(sum(5, 10));
3. 编写Java代码
接下来,在Java项目中创建一个类,比如 NodeJsExecutor.java
,并编写以下代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.nio.file.Paths;
public class NodeJsExecutor {
public static void main(String[] args) {
// JavaScript文件路径
Path jsFilePath = Paths.get("path/to/your/sum.js");
// 构建ProcessBuilder
ProcessBuilder processBuilder = new ProcessBuilder(
"node", // 进行调用的本地node,注意本机的命令行可以进行执行node命令
jsFilePath // 传递JavaScript文件路径给Node.js
);
try {
// 启动Node.js进程
Process process = processBuilder.start();
// 从Node.js进程的标准输出读取结果
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
// 存储结果
StringBuilder result = new StringBuilder();
while ((line = reader.readLine()) != null) {
result.append(line).append("\n");
}
// 进行打印脚本执行的结果
System.out.println(result);
// 确保进程已经结束
int exitCode = process.waitFor();
System.out.println("Node.js process exited with code " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
请确保将 "path/to/your/sum.js"
替换为您的 sum.js
文件的实际路径。
3. 运行Java程序
编译并运行 NodeJsExecutor
类。如果一切顺利,您将看到控制台输出了 sum.js
脚本的执行结果。
// 正常执行结果
15
Node.js process exited with code 0
// 错误运行
Node.js process exited with code 1 // 进程码返回1 标识执行的js文件代码书写错误
封装好的Util
1.引入相关依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version>
</dependency>
2. ScriptUtils
package com.fs.utils;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.StandardCopyOption;
// 脚本工具类
public class MyScriptUtils {
/**
* 自述:执行脚本工具类,通过本机的脚本环境进行执行相应的脚本语言.
* 使用前请进行检查本机的安装对应的脚本环境.
* 如:node -v , python 出现对应的版本号,说明环境已安装.
*/
public static String CHARSET = "utf-8";
public static final String NODE = "node";
/**
* 通过本地的nodejs进行执行js脚本文件后, 返回执行结果
* @param scriptPath 脚本的路径
* @param functionName 要执行的脚本中的函数名
* @param params 函数的参数
* @return 函数的执行结果 String
*/
public static String execJS(String scriptPath, String functionName,String... params){
// 对传入的路径进行处理
String template = scriptPath.replace(".js", "tem.js") ;
// 进行拷贝一份临时脚本文件
File temFile = FileUtil.copyFile(scriptPath, template, StandardCopyOption.REPLACE_EXISTING);
// 进行清除复制文件中的console.log()方法
String content = clearConsoleS(temFile);
// 进行写入到临时文件
FileUtil.writeString(content, temFile, CHARSET);
// 脚本函数传递的参数
String args = ArrayUtil.join(params, ",");
//追加到脚本的内容
String script=StrUtil.format("console.log({}({}))", functionName, args);
// 进行追加到文件
FileUtil.appendString(script, temFile,CHARSET);
// 进行执行脚本
String scriptResult = scriptProcess(temFile, NODE);
// 删除临时文件
FileUtil.del(temFile);
return scriptResult;
}
/**
* 进行清除js脚本中的所有打印到控制台的语句console;
* @param script 字符串脚本
* @return 清除后的脚本
*/
public static String clearConsoleS( String script) {
return script.replaceAll("(console.log\\()(.*)(\\))", "@@")
.replaceAll("@@;", "")
.replaceAll("@@", "");
}
/**
* 对传入的脚本文件进行清除所有的console
* @param scriptFile 脚本文件
* @return 清除后的脚本字符串
*/
@org.jetbrains.annotations.NotNull
public static String clearConsoleS(File scriptFile) {
return FileUtil.readString(scriptFile, StandardCharsets.UTF_8)
.replaceAll("(console.log\\()(.*)(\\))", "@@")
.replaceAll("@@;", "")
.replaceAll("@@", "");
}
/**
* 执行脚本文件的程序,错误输出源脚本执行环境错误输出
* @param scriptFile 脚本文件的路径
* @param processName 执行的程序名字,例如 node ,python ,php等
* @return 执行在控制台的结果 String
*/
public static String scriptProcess(File scriptFile,String processName){
// 进行构建一个脚本执行执行程序
ProcessBuilder processBuilder = new ProcessBuilder(
processName,
scriptFile.getAbsolutePath() // 获取脚本文件路径给Node.js
);
try {
// 进行启动脚本进程
Process process = processBuilder.start();
// 创建用于读取进程标准输出的缓冲流
BufferedReader outputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
// 创建用于读取进程错误输出的缓冲流
BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
// 进行存储脚本执行结果
StringBuilder result = new StringBuilder();
// 创建一个进行进行获取脚本执行结果的线程
Thread outputThread = new Thread(() -> {
String line;
try {
while ((line = outputReader.readLine()) != null) {
result.append(line).append("\n");
}
} catch (IOException e) {
e.printStackTrace();
}
});
// 创建一个进行进行获取脚本执行错误的线程
Thread errorThread = new Thread(() -> {
String line;
try {
while ((line = errorReader.readLine()) != null) {
System.err.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
});
// 启动线程
outputThread.start();
errorThread.start();
// 等待线程完成
outputThread.join();
errorThread.join();
// 确保程序进程已经结束
process.waitFor();
return result.toString();
}catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return null;
}
// 脚本测试
public static void main(String[] args) {
String jsFilePath = "src/main/resources/js/sum.js"; // 替换为你的JavaScript文件路径
String s = execJS(jsFilePath, "sum","1","2");
System.out.println(s);
}
}
总结
在尝试了多种在Java中运行JavaScript代码的方法后,我们选择了通过Java调用外部Node.js进程的方法。虽然这种方法可能不是最高效的,但它提供了良好的兼容性和稳定性。在本教程中,我们展示了如何执行外部Node.js脚本,并通过Java代码读取其输出。希望本教程能帮助您解决在Java应用程序中集成Node.js脚本的需求,并为您提供一个可靠的解决方案。如果您有任何问题或需要进一步的帮助,请随时提问。