package net.srt.utils.code;
import cn.hutool.core.io.FileUtil;
import net.srt.flink.common.context.SpringContextUtils;
import net.srt.utils.loader.DynamicClassLoader;
import net.srt.utils.loader.JavaSourceFromString;
import srt.cloud.framework.dbswitch.common.util.StringUtil;
import javax.tools.*;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* @author zdc
* @description 动态代码工具类
* @date 2025/4/24 15:34
*/
public class CodeUtil {
private static final String ALLOWED_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
public static Boolean compile(String code, String className) {
return compile(null, code, className);
}
/**
* @param classPath 编译后的存放路径
* @param code 动态代码
* @return
*/
public static Boolean compile(String classPath, String code, String className) {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
JavaFileObject source = new JavaSourceFromString(className, code);
Iterable<? extends JavaFileObject> compilationUnits = Collections.singletonList(source);
List<String> options;
if (classPath != null) {
String jarPath = SpringContextUtils.getProperty("dynamic.jar.path");
if (StringUtil.isBlank(jarPath)) {
jarPath = Objects.requireNonNull(CodeUtil.class.getResource("/")).getPath().substring(1) + "generated-resources/appassembler/jsw";
}
options = Arrays.asList("-d", classPath, "-classpath", getJarFiles(jarPath));
} else {
options = Collections.emptyList();
}
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, compilationUnits);
Boolean call = task.call();
if (!call) {
for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) {
System.err.println("Error on line " + diagnostic.getLineNumber() + ": " + diagnostic);
}
}
try {
fileManager.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
return call;
}
/**
* 查找该目录下的所有的jar文件
*
* @param jarPath
* @throws Exception
*/
private static String getJarFiles(String jarPath) {
StringBuilder jars = new StringBuilder();
List<File> files = FileUtil.loopFiles(jarPath, pathname -> {
if (pathname.isDirectory()) {
return true;
} else {
return pathname.getName().endsWith(".jar");
}
});
for (File file : files) {
jars.append(file.getPath()).append(";");
}
return jars.toString();
}
public static Class getClass(String classPath, String code, String packagePath) {
return getClass(classPath, code, packagePath, null);
}
public static Class getClass(String classPath, String code, String packagePath, String randomClassName) {
if (randomClassName == null) {
randomClassName = generateRandomClassName();
}
DynamicClassLoader loader = new DynamicClassLoader(classPath);
Class<?> loadedClass = null;
try {
loadedClass = loader.loadClass(packagePath + "." + randomClassName);
} catch (ClassNotFoundException e) {/*e.printStackTrace();*/}
if (loadedClass != null) {
return loadedClass;
}
String oldClassName = code.substring(code.indexOf("class") + 5, code.indexOf("{"));
code = code.replaceAll("\\bclass\\s+" + oldClassName.trim() + "\\b", "class " + randomClassName);
if (compile(classPath, code, randomClassName)) {
try {
System.out.println("动态代码编译通过");
return loader.loadClass(packagePath + "." + randomClassName);
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
System.out.println("动态代码编译失败");
}
return null;
}
/**
* 执行无参数方法
*
* @param classPath
* @param code
* @param methodName
* @return
*/
public static Object run(String classPath, String code, String packagePath, String methodName) {
try {
Class clazz = getClass(classPath, code, packagePath);
if (clazz != null) {
Method method = clazz.getMethod(methodName);
return method.invoke(clazz.getDeclaredConstructor().newInstance());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
/**
* 执行有参数方法
*
* @param classPath
* @param code
* @param methodName
* @param args
* @param parameterTypes
* @return
*/
public static Object runParam(String classPath, String code, String packagePath, String methodName, List<Object> args, Class<?>... parameterTypes) {
try {
Class clazz = getClass(classPath, code, packagePath);
if (clazz != null) {
Method method = clazz.getMethod(methodName, parameterTypes);
return method.invoke(clazz.getDeclaredConstructor().newInstance(), args.toArray());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
public static Object runParam(String classPath, String code, String packagePath, String methodName, String randomClassName, List<Object> args, Class<?>... parameterTypes) {
try {
Class clazz = getClass(classPath, code, packagePath, randomClassName);
if (clazz != null) {
Method method = clazz.getMethod(methodName, parameterTypes);
return method.invoke(clazz.getDeclaredConstructor().newInstance(), args.toArray());
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
public static String generateRandomClassName() {
SecureRandom random = new SecureRandom();
StringBuilder sb = new StringBuilder();
sb.append("Dynamic_Rule_");
// 首字母大写
sb.append((char) ('D' + random.nextInt(26)));
// 生成后续字符
for (int i = 0; i < 15; i++) {
sb.append(ALLOWED_CHARS.charAt(random.nextInt(ALLOWED_CHARS.length())));
}
return sb.toString().replaceAll("[^" + ALLOWED_CHARS + "]", "");
}
public static void main(String[] args) {
String code = "public class DynamicClass {\n" +
" public String execute() {\n" +
" String result = sayHello() + calculate(3, 5);\n" +
" return \"[执行结果] \" + result;\n" +
" }\n" +
" public String execute(String param) {\n" +
" String result = sayHello() + calculate(3, 5);\n" +
" return \"[执行结果] \" + result+param;\n" +
" }\n" +
" private String sayHello() {\n" +
" return \"Hello, 动态代码! \";\n" +
" }\n" +
" private String calculate(int a, int b) {\n" +
" return \"计算结果: \" + (a + b);\n" +
" }\n" +
"}";
Object execute = run("E:\\codes", code, "", "execute");
System.out.println(execute);
}
}