JavaFileObject 对象:表示要编译的 Java 源代码。JavaFileObject 是一个抽象类,用于表示 Java 源代码或字节码。JavaFileObject 有多种实现方式,例如 SimpleJavaFileObject、JavaFileObjectWrapper 等。
public class DynamicCompiler { public static void main(String[] args) throws Exception { // 堆代码 duidaima.com // 创建 JavaCompiler 对象 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 创建 DiagnosticCollector 对象,用于收集编译时的诊断信息 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>(); // 创建 JavaFileManager 对象,用于管理编译过程中的文件 StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null); // 创建 JavaFileObject 对象,用于表示要编译的 Java 源代码 String code = "public class HelloWorld { public static void main(String[] args) { System.out.println(\"Hello World!\"); } }"; JavaFileObject source = new JavaSourceFromString("HelloWorld", code); // 获取 CompilationTask 对象 Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(source); CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits); // 编译 Java 源代码 boolean success = task.call(); // 获取诊断信息 List<Diagnostic<? extends JavaFileObject>> messages = diagnostics.getDiagnostics(); for (Diagnostic<? extends JavaFileObject> message : messages) { System.out.println(message.getMessage(null)); } // 处理编译结果 if (success) { System.out.println("Compilation was successful."); } else { System.out.println("Compilation failed."); } fileManager.close(); } } 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; } }运行结果:
Hello World! Compilation was successful.二、如何结合 springboot 项目使用
// 堆代码 duidaima.com // 通过调用这个方法即可实现 java 的动态编译功能啦 public static Class compile(String className, String code) { try (MemoryClassLoader loader = MemoryClassLoader.genInstance()) { loader.registerJava(className, code); return MemoryClassLoader.getInstance().loadClass(className); } catch (Exception e) { // ignore } } } public class MemoryClassLoader extends URLClassLoader { private static final Map<String, byte[]> classBytes = new ConcurrentHashMap<>(); private MemoryClassLoader() { super(new URL[0], MemoryClassLoader.class.getClassLoader()); } private static final Map<String, MemoryClassLoader> CLASSLOADER_MAP = new ConcurrentHashMap<String, MemoryClassLoader>() {{ put(KEY_CLASSLOADER, new MemoryClassLoader()); }}; private static final String KEY_CLASSLOADER = "key_classloader"; /** * 注册 Java 字符串到内存类加载器中 */ public void registerJava(String className, String javaCode) { try { Map<String, byte[]> compile = compile(className, javaCode); if (null != compile) { classBytes.putAll(compile); } } catch (Exception e) { e.printStackTrace(); } } /** * 编译 Java 代码 */ private static Map<String, byte[]> compile(String className, String javaCode) { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager stdManager = getStandardFileManager(null, null, null); try (MemoryJavaFileManager manager = new MemoryJavaFileManager(stdManager)) { JavaFileObject javaFileObject = manager.makeStringSource(className, javaCode); JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, null, null, Collections.singletonList(javaFileObject)); Boolean result = task.call(); if (result != null && result) { return manager.getClassBytes(); } } return null; } @Override public Class<?> findClass(String name) throws ClassNotFoundException { byte[] buf = classBytes.get(name); if (buf == null) { return super.findClass(name); } return defineClass(name, buf, 0, buf.length); } @Override public void close() { classBytes.clear(); CLASSLOADER_MAP.clear(); } /** * 自定义 Java 文件管理器 */ public static SpringJavaFileManager getStandardFileManager(DiagnosticListener<? super JavaFileObject> var1, Locale var2, Charset var3) { Context var4 = new Context(); var4.put(Locale.class, var2); if (var1 != null) { var4.put(DiagnosticListener.class, var1); } PrintWriter var5 = var3 == null ? new PrintWriter(System.err, true) : new PrintWriter(new OutputStreamWriter(System.err, var3), true); var4.put(Log.outKey, var5); return new SpringJavaFileManager(var4, true, var3); } /** * 获取实例 */ public static MemoryClassLoader getInstance() { return CLASSLOADER_MAP.get(KEY_CLASSLOADER); } /** * 生成新的实例 */ public static MemoryClassLoader genInstance() { MemoryClassLoader classLoader = new MemoryClassLoader(); CLASSLOADER_MAP.put(KEY_CLASSLOADER, new MemoryClassLoader()); return classLoader; } public static String getPath() { ApplicationHome home = new ApplicationHome(MemoryJavaFileManager.class); String path = home.getSource().getPath(); return path; } public static boolean isJar() { return getPath().endsWith(".jar"); } } class MemoryJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> { // compiled classes in bytes: final Map<String, byte[]> classBytes = new HashMap<>(); final Map<String, List<JavaFileObject>> classObjectPackageMap = new HashMap<>(); private JavacFileManager javaFileManager; /** * key 包名 value javaobj 主要给 jdk 编译 class 的时候找依赖 class 用 */ public final static Map<String, List<JavaFileObject>> CLASS_OBJECT_PACKAGE_MAP = new HashMap<>(); private static final Object lock = new Object(); private boolean isInit = false; public void init() { try { String jarBaseFile = MemoryClassLoader.getPath(); JarFile jarFile = new JarFile(new File(jarBaseFile)); List<JarEntry> entries = jarFile.stream().filter(jarEntry -> jarEntry.getName().endsWith(".jar")).collect(Collectors.toList()); JarFile libTempJarFile; List<JavaFileObject> onePackageJavaFiles; String packageName; for (JarEntry entry : entries) { libTempJarFile = jarFile.getNestedJarFile(jarFile.getEntry(entry.getName())); if (libTempJarFile.getName().contains("tools.jar")) { continue; } Enumeration<JarEntry> tempEntriesEnum = libTempJarFile.entries(); while (tempEntriesEnum.hasMoreElements()) { JarEntry jarEntry = tempEntriesEnum.nextElement(); String classPath = jarEntry.getName().replace("/", "."); if (!classPath.endsWith(".class") || jarEntry.getName().lastIndexOf("/") == -1) { continue; } else { packageName = classPath.substring(0, jarEntry.getName().lastIndexOf("/")); onePackageJavaFiles = CLASS_OBJECT_PACKAGE_MAP.containsKey(packageName) ? CLASS_OBJECT_PACKAGE_MAP.get(packageName) : new ArrayList<>(); onePackageJavaFiles.add(new MemorySpringBootInfoJavaClassObject(jarEntry.getName().replace("/", ".").replace(".class", ""), new URL(libTempJarFile.getUrl(), jarEntry.getName()), javaFileManager)); CLASS_OBJECT_PACKAGE_MAP.put(packageName, onePackageJavaFiles); } } } } catch (Exception e) { e.printStackTrace(); } isInit = true; } MemoryJavaFileManager(JavaFileManager fileManager) { super(fileManager); this.javaFileManager = (JavacFileManager) fileManager; } public Map<String, byte[]> getClassBytes() { return new HashMap<>(this.classBytes); } @Override public void flush() { } @Override public void close() { classBytes.clear(); classObjectPackageMap.clear(); CLASS_OBJECT_PACKAGE_MAP.clear(); } public List<JavaFileObject> getLibJarsOptions(String packgeName) { synchronized (lock) { if (!isInit) { init(); } } return CLASS_OBJECT_PACKAGE_MAP.get(packgeName); } @Override public Iterable<JavaFileObject> list(Location location,String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException { if ("CLASS_PATH".equals(location.getName()) && MemoryClassLoader.isJar()) { List<JavaFileObject> result = getLibJarsOptions(packageName); if (result != null) { return result; } } Iterable<JavaFileObject> it = super.list(location, packageName, kinds, recurse); if (kinds.contains(JavaFileObject.Kind.CLASS)) { final List<JavaFileObject> javaFileObjectList = classObjectPackageMap.get(packageName); if (javaFileObjectList != null) { if (it != null) { for (JavaFileObject javaFileObject : it) { javaFileObjectList.add(javaFileObject); } } return javaFileObjectList; } else { return it; } } else { return it; } } @Override public String inferBinaryName(Location location, JavaFileObject file) { if (file instanceof MemoryInputJavaClassObject) { return ((MemoryInputJavaClassObject) file).inferBinaryName(); } return super.inferBinaryName(location, file); } @Override public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { if (kind == JavaFileObject.Kind.CLASS) { return new MemoryOutputJavaClassObject(className); } else { return super.getJavaFileForOutput(location, className, kind, sibling); } } JavaFileObject makeStringSource(String className, final String code) { String classPath = className.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension; return new SimpleJavaFileObject(URI.create("string:///" + classPath), JavaFileObject.Kind.SOURCE) { @Override public CharBuffer getCharContent(boolean ignoreEncodingErrors) { return CharBuffer.wrap(code); } }; } void makeBinaryClass(String className, final byte[] bs) { JavaFileObject javaFileObject = new MemoryInputJavaClassObject(className, bs); String packageName = ""; int pos = className.lastIndexOf('.'); if (pos > 0) { packageName = className.substring(0, pos); } List<JavaFileObject> javaFileObjectList = classObjectPackageMap.get(packageName); if (javaFileObjectList == null) { javaFileObjectList = new LinkedList<>(); javaFileObjectList.add(javaFileObject); classObjectPackageMap.put(packageName, javaFileObjectList); } else { javaFileObjectList.add(javaFileObject); } } class MemoryInputJavaClassObject extends SimpleJavaFileObject { final String className; final byte[] bs; MemoryInputJavaClassObject(String className, byte[] bs) { super(URI.create("string:///" + className.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS); this.className = className; this.bs = bs; } @Override public InputStream openInputStream() { return new ByteArrayInputStream(bs); } public String inferBinaryName() { return className; } } class MemoryOutputJavaClassObject extends SimpleJavaFileObject { final String className; MemoryOutputJavaClassObject(String className) { super(URI.create("string:///" + className.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS); this.className = className; } @Override public OutputStream openOutputStream() { return new FilterOutputStream(new ByteArrayOutputStream()) { @Override public void close() throws IOException { out.close(); ByteArrayOutputStream bos = (ByteArrayOutputStream) out; byte[] bs = bos.toByteArray(); classBytes.put(className, bs); makeBinaryClass(className, bs); } }; } } } class MemorySpringBootInfoJavaClassObject extends BaseFileObject { private final String className; private URL url; MemorySpringBootInfoJavaClassObject(String className, URL url, JavacFileManager javacFileManager) { super(javacFileManager); this.className = className; this.url = url; } @Override public Kind getKind() { return Kind.valueOf("CLASS"); } @Override public URI toUri() { try { return url.toURI(); } catch (URISyntaxException e) { e.printStackTrace(); } return null; } @Override public String getName() { return className; } @Override public InputStream openInputStream() { try { return url.openStream(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override public OutputStream openOutputStream() throws IOException { return null; } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return null; } @Override public Writer openWriter() throws IOException { return null; } @Override public long getLastModified() { return 0; } @Override public boolean delete() { return false; } @Override public String getShortName() { return className.substring(className.lastIndexOf(".")); } @Override protected String inferBinaryName(Iterable<? extends File> iterable) { return className; } @Override public boolean equals(Object o) { return false; } @Override public int hashCode() { return 0; } @Override public boolean isNameCompatible(String simpleName, Kind kind) { return false; } } // 自定义 springboot 的类加载器 class SpringJavaFileManager extends JavacFileManager { public SpringJavaFileManager(Context context, boolean b, Charset charset) { super(context, b, charset); } @Override public ClassLoader getClassLoader(Location location) { nullCheck(location); Iterable var2 = this.getLocation(location); if (var2 == null) { return null; } else { ListBuffer var3 = new ListBuffer(); Iterator var4 = var2.iterator(); while (var4.hasNext()) { File var5 = (File) var4.next(); try { var3.append(var5.toURI().toURL()); } catch (MalformedURLException var7) { throw new AssertionError(var7); } } return this.getClassLoader((URL[]) var3.toArray(new URL[var3.size()])); } } protected ClassLoader getClassLoader(URL[] var1) { ClassLoader var2 = this.getClass().getClassLoader(); try { Class loaderClass = Class.forName("org.springframework.boot.loader.LaunchedURLClassLoader"); Class[] var4 = new Class[]{URL[].class, ClassLoader.class}; Constructor var5 = loaderClass.getConstructor(var4); return (ClassLoader) var5.newInstance(var1, var2); } catch (Throwable var6) { } return new URLClassLoader(var1, var2); } }总结