动态编译和加载外部Java类的核心流程可以概括为以下几个步骤:
javac
命令行工具或者使用Java API中的编译器API(如javax.tools.JavaCompiler
)来实现。ClassLoader
子类并重写其loadClass
方法来实现。在这个方法中,你可以从文件系统、网络或其他来源读取字节码,并使用defineClass
方法将其定义为一个Class
对象。newInstance
方法来创建类的实例,并调用其方法。package com.artisan.jsr269;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* @author 小工匠
* @version 1.0
* @mark: show me the code , change the world
*/
public class DynamicCompiler {
public static void main(String[] args) throws Exception {
//创建源文件
String currentDir = System.getProperty("user.dir") + "/boot-beanUtils" ;
// 定义一个简单的Java类,包含一个方法,该方法打印出“Hello Artisan”
String src = "package com.artisan.jsr269 ;"
+ "public class ArtisanComplier {"
+ " public void methodA() {"
+ " System.out.println(\"Hello Artisan\");"
+ "}}";
// 源文件路径和名称
String filename = currentDir + "/src/main/java/com/artisan/jsr269/ArtisanComplier.java";
File file = new File(filename);
// 确保源文件所在的目录存在
File fileParent = file.getParentFile();
if (!fileParent.exists()) {
fileParent.mkdir();
}
// 确保源文件存在
if (!file.exists()) {
file.createNewFile();
}
// 将源代码写入文件
FileWriter fw = new FileWriter(file);
fw.write(src);
fw.flush();
fw.close();
// 使用JavaCompiler 编译java文件
// 获取系统Java编译器
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
// 获取标准文件管理器
StandardJavaFileManager fileManager = jc.getStandardFileManager(null, null, null);
// 获取要编译的文件对象
Iterable fileObjects = fileManager.getJavaFileObjects(filename);
// 创建编译任务
JavaCompiler.CompilationTask cTask = jc.getTask(null, fileManager, null, null, null, fileObjects);
// 执行编译任务
cTask.call();
// 关闭文件管理器
fileManager.close();
// 使用URLClassLoader加载class到内存
URL[] urls = new URL[]{new URL("file:/" + currentDir + "/src/main/java/com/artisan/jsr269/ArtisanComplier.java")};
URLClassLoader cLoader = new URLClassLoader(urls);
// 加载类
Class c = cLoader.loadClass("com.artisan.jsr269.ArtisanComplier");
// 关闭类加载器
cLoader.close();
// TODO 在这之前要确保编译任务完成,否则这里通过反射实例化会报错
// 利用class创建实例,反射执行方法
Object obj = c.newInstance();
// 获取类中的方法
Method method = c.getMethod("methodA");
// 执行方法
method.invoke(obj);
}
}
运行结果