Java使用ProcessBuilder执行动态Java文件时找不到主类问题求解
问题背景
我开发了一个接收(code, language)负载的API,会生成随机文件名,根据语言类型匹配后缀后通过Java保存文件,UI传入的负载示例如下:
{"code": "class Demo{ \r\n" + " public static void main(String args[]){ \r\n" + " System.out.println(\"Hello Java\"); \r\n" + " } \r\n" + "}", "language": "java"}
文件可正常保存到C:\\temp路径下,但使用Java的ProcessBuilder执行这些文件时报错Error: Could not find or load main class BNkHZk,原因是随机生成的Java文件名(如BNkHZk.java)与代码中声明的类名不一致,编译后生成的class文件名为Demo.class,调用java命令执行时传入随机文件名就会触发该错误。
现有核心代码如下:
ProcessBuilder processBuilder = new ProcessBuilder(new String[] { "javac", fullPath }); //fullPath是C:\temp\BNkHZk.java Process process = processBuilder.start();
private void ExecuteCode(String language, String code){ String tempFileName = RandomStringUtils.randomAlphanumeric(6); String fullPath = "C:\\temp\\" + tempFileName+ "." + language; FileWriter fileWriter = null; try { fileWriter = new FileWriter(new File(fullPath)); fileWriter.write(code); } catch (Exception e) { System.out.println("IO Exception while creating new file"); e.printStackTrace(); } finally { if (fileWriter != null) { try { fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } } ProcessBuilder processBuilder = new ProcessBuilder(new String[] { "javac", fullPath }); Process process = processBuilder.start(); if (process.getErrorStream().read() != -1) { BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream())); String line = ""; while ((line = reader.readLine()) != null) { System.out.println(line); } } int exitcode = -1; try { exitcode = process.waitFor(); } catch (InterruptedException e) { e.printStackTrace(); } if (exitcode == 0) { processBuilder = new ProcessBuilder(new String[] { "java","tempFileName"});//BNkHZk Process process1 = processBuilder.start(); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line = ""; while ((line = reader.readLine()) != null) { System.out.println(line + "\n"); } } }
由于我接收的代码内容为字符串形式,无法提前获知类名以保证文件名与类名一致,请问有什么可行的解决方案?
可行解决方案
- 方案1:使用Java 11+单文件源码直接执行特性(改造成本最低)
Java 11及以上版本支持直接运行单文件Java源码,无需提前执行javac编译,也不要求文件名和类名一致,直接修改执行命令即可:
if (exitcode == 0) { // 直接传入java源码文件路径执行 processBuilder = new ProcessBuilder(new String[] { "java", fullPath }); Process process1 = processBuilder.start(); // 注意这里要读取执行进程process1的输出流,原代码读取的是编译进程的流属于逻辑错误 BufferedReader reader = new BufferedReader(new InputStreamReader(process1.getInputStream())); String line = ""; while ((line = reader.readLine()) != null) { System.out.println(line + "\n"); } }
- 方案2:动态替换代码类名匹配随机文件名(兼容低版本Java)
在保存代码到文件前,通过正则匹配替换代码中的类名为生成的随机文件名,保证两者完全一致:
String tempFileName = RandomStringUtils.randomAlphanumeric(6); // 替换类名为随机文件名 Pattern classPattern = Pattern.compile("class\\s+(\\w+)\\s*\\{", Pattern.DOTALL); Matcher matcher = classPattern.matcher(code); if (matcher.find()) { code = code.replaceFirst("class\\s+\\w+\\s*\\{", "class " + tempFileName + " {"); } // 再执行后续保存、编译、执行逻辑即可,原有逻辑无需修改
- 方案3:提取代码中的主类名执行
通过正则匹配提取代码中包含main方法的类名,执行时直接传入该类名,注意要指定进程工作目录为class文件所在路径:
// 编译成功后提取主类名 Pattern mainPattern = Pattern.compile("class\\s+(\\w+)\\s*\\{.*public\\s+static\\s+void\\s+main\\s*\\(", Pattern.DOTALL); Matcher mainMatcher = mainPattern.matcher(code); if (mainMatcher.find()) { String mainClassName = mainMatcher.group(1); processBuilder = new ProcessBuilder(new String[] { "java", mainClassName }); // 指定工作目录为class文件所在路径 processBuilder.directory(new File("C:\\temp\\")); Process process1 = processBuilder.start(); // 读取输出逻辑同上 }
内容的提问来源于stack exchange,提问作者Ullas Sharma
相关产品推荐
相关产品推荐

