JAVA运行Script脚本python脚本等

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脚本的需求,并为您提供一个可靠的解决方案。如果您有任何问题或需要进一步的帮助,请随时提问。

  • 22
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值