一、解决思路
在Java中,直接执行传入的代码字符串不是一个简单的任务,因为Java是一个编译型语言,这意味着在运行代码之前,你需要将代码编译成字节码。然而,你可以使用一些高级技术,比如Java的反射API、Java Compiler API或者脚本引擎,例如Nashorn引擎,来动态编译和执行代码。
二、使用场景
1.力扣、牛客等让用户输入代码可以执行的场景
2.对接口的返回进行动态校验的场景,可配合nacos这些配置中心来使用
3.其他需要执行用户传入代码块的场景…
三、下面是针对场景2的demo案例
package com.lzl.demo2;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
/**
* @Author: Luzelong
* @Created: 2024/2/19 17:51
*/
public class TestInvokeCode {
public static void main(String[] args) throws Exception {
//模拟调用第三方接口的返回
String response = "{\"success\": true, \"error\": null, \"data\" : 8}";
// 第二个参数可来源于 读取配置中心的配置
runCode(response, "import java.util.*;" +
"import com.alibaba.fastjson.JSON;" +
"import com.alibaba.fastjson.JSONObject;\n" +
"public class DynamicCode {" +
" public static void execute(String response) {" +
" JSONObject jsonObject = JSON.parseObject(response);" +
" System.out.println(jsonObject.getInteger(\"data\") > 4);" +
" }" +
"}");
}
//对返回的接口进行处理,处理逻辑需要依靠用户编写的code
private static void runCode(String response, String code) throws Exception {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
if (compiler == null) {
throw new IllegalStateException("Compiler not available");
}
// Define a file object for the source code
InMemoryJavaFileObject fileObject = new InMemoryJavaFileObject("DynamicCode", code);
// Compile the source code
Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(fileObject);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
JavaFileManager fileManager = new ForwardingJavaFileManager(compiler.getStandardFileManager(null, null, null)) {
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
return new SimpleJavaFileObject(URI.create("string:///" + className.replace('.', '/') + kind.extension), kind) {
@Override
public OutputStream openOutputStream() throws IOException {
return outputStream;
}
};
}
};
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits);
boolean compilationResult = task.call();
if (!compilationResult) {
throw new IllegalStateException("Compilation failed");
}
// Load the compiled class
ClassLoader classLoader = new InMemoryClassLoader(outputStream.toByteArray());
Class<?> dynamicClass = classLoader.loadClass("DynamicCode");
Method executeMethod = dynamicClass.getMethod("execute", String.class);
// Invoke the 'execute' method on the loaded class
executeMethod.invoke(null, response);
}
static class InMemoryJavaFileObject extends SimpleJavaFileObject {
final String code;
InMemoryJavaFileObject(String name, String code) {
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return code;
}
}
static class InMemoryClassLoader extends ClassLoader {
private final byte[] classBytes;
InMemoryClassLoader(byte[] classBytes) {
this.classBytes = classBytes;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return defineClass(name, classBytes, 0, classBytes.length);
}
}
}