JDK 6 引入 JavaCompiler API,支持運行時動態編譯 Java 源碼。適用場景:動態業務規則、插件熱加載、模板引擎性能不足時的替代方案。
// 1. 獲取編譯器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
// 2. 文件管理器(控制源碼路徑、classpath、輸出目錄)
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
// 3. 構建編譯任務
Iterable extends JavaFileObject> compilationUnits = ...; // 源碼單元
CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits);
// 4. 執行編譯
boolean success = task.call();
需實現 JavaFileObject,將字符串封裝為編譯單元:
import javax.tools.SimpleJavaFileObject;
import java.net.URI;
public class JavaSourceFromString extends SimpleJavaFileObject {
final String code;
JavaSourceFromString(String name, String code) {
super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) {
return code;
}
}
public class TestDyCompile {
public static void main(String[] args) throws Exception {
// 1. 動態生成類源碼
String classCode = "package dyclass; public class Foo implements Test {"
+ "public void test() { System.out.println(\"Foo2\"); } }";
// 2. 編譯配置
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm = jc.getStandardFileManager(null, null, null);
fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File("bin/")));
// 3. 構建編譯單元
JavaFileObject jfo = new JavaSourceFromString("dyclass.Foo", classCode);
Iterable extends JavaFileObject> units = Arrays.asList(jfo);
// 4. 編譯並加載
if (jc.getTask(null, fm, null, null, null, units).call()) {
Test t = (Test) Class.forName("dyclass.Foo").newInstance();
t.test(); // 輸出:Foo2
}
}
}
interface Test { void test(); }
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, ...) 指定 .class 輸出路徑,否則默認項目根目錄導致加載失敗ClassLoader 隔離動態類,支持熱卸載