eclipse编译java项目class文件_动态编译 Java 代码以及生成 Jar 文件
導(dǎo)讀: 最近在看 Flink 源碼的時候發(fā)現(xiàn)到一段實用的代碼,該代碼實現(xiàn)了 java 動態(tài)編譯以及生成 jar 文件。將其進(jìn)行改進(jìn)后可以應(yīng)用到我們的平臺上,實現(xiàn)在平臺頁面上編寫 java 代碼語句,提交后由后臺進(jìn)行編譯和打成 Jar 包再上傳到指定的文件存儲系統(tǒng),從而代替之前在本地自己手動打 UDF 包的方式。下面我將對這段代碼做一些簡單分析,希望對各位有所幫助。
核心代碼
public class TestUserClassLoaderJar { private static final String GENERATED_UDF_CLASS = "LowerUDF"; private static final String GENERATED_UDF_CODE = "public class " + GENERATED_UDF_CLASS + " extends extends org.apache.flink.table.functions.ScalarFunction {" + " public String eval(String str) {" + " return str.toLowerCase();" + " }" + "}"; /** * 將生成的 UDF class 打包到 JAR 中并且返回 JAR 所在的路徑. */ public static File createJarFile(File tmpDir, String jarName) throws IOException { // 創(chuàng)建一個 java 文件 File javaFile = Paths.get(tmpDir.toString(), GENERATED_UDF_CLASS + ".java").toFile(); javaFile.createNewFile(); // 將代碼寫入 java 文件中 FileUtils.writeFileUtf8(javaFile, GENERATED_UDF_CODE); // 編譯 java文件生成 class 文件 DiagnosticCollector diagnostics = new DiagnosticCollector<>(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null); Iterable extends JavaFileObject> compilationUnit = fileManager.getJavaFileObjectsFromFiles(Collections.singletonList(javaFile)); JavaCompiler.CompilationTask task = compiler.getTask( null, fileManager, diagnostics, Collections.emptyList(), null, compilationUnit); // 此處結(jié)果返回一個布爾值,可用于判斷是否編譯成功及是否執(zhí)行下面的打包操作 task.call(); // 將 class 文件打包到 Jar 中 File classFile = Paths.get(tmpDir.toString(), GENERATED_UDF_CLASS + ".class").toFile(); File jarFile = Paths.get(tmpDir.toString(), jarName).toFile(); JarOutputStream jos = new JarOutputStream(new FileOutputStream(jarFile)); JarEntry jarEntry = new JarEntry(GENERATED_UDF_CLASS + ".class"); jos.putNextEntry(jarEntry); byte[] classBytes = FileUtils.readAllBytes(classFile.toPath()); jos.write(classBytes); jos.closeEntry(); jos.close(); return jarFile; } public static void main(String[] args) throws IOException { createJarFile(new File("G:jarSave"),"test.jar"); }}以上代碼主要完成以下三步操作:
- 創(chuàng)建一個 .java 文件,并將外部輸入的 java 語句寫入到該文件中
- 對 java 文件進(jìn)行編譯并生成 class 文件
- 將 class 文件打包到 JAR 中并返回 JAR 的路徑
下圖是動態(tài)編譯的幾個關(guān)鍵類的創(chuàng)建方式及作用:
JavaCompiler 的由來
在上面代碼中通過 ToolProvider.getSystemJavaCompiler() 獲取到 JavaCompiler。深入內(nèi)部 findSystemToolClass() 方法發(fā)現(xiàn)其最終先是通過 System.getProperty("java.home") 獲取到 /jdk1.8.0_241/jre 目錄,再獲取其上級目錄中 lib 目錄下的 tools.jar(也就是/jdk1.8.0_241/lib/ tools.jar),并進(jìn)行動態(tài)加載 Jar 獲取到 JavaCompiler。
findSystemToolClass 代碼片段:
private Class> findSystemToolClass(String toolClassName) throws MalformedURLException, ClassNotFoundException { // try loading class directly, in case tool is on the bootclasspath try { return Class.forName(toolClassName, false, null); } catch (ClassNotFoundException e) { trace(FINE, e); // if tool not on bootclasspath, look in default tools location (tools.jar) ClassLoader cl = (refToolClassLoader == null ? null : refToolClassLoader.get()); if (cl == null) { File file = new File(System.getProperty("java.home")); if (file.getName().equalsIgnoreCase("jre")) file = file.getParentFile(); for (String name : defaultToolsLocation) file = new File(file, name); // if tools not found, no point in trying a URLClassLoader // so rethrow the original exception. if (!file.exists()) throw e; URL[] urls = { file.toURI().toURL() }; trace(FINE, urls[0].toString()); cl = URLClassLoader.newInstance(urls); refToolClassLoader = new WeakReference(cl); } return Class.forName(toolClassName, false, cl); } }補(bǔ)充:FileUtils 工具類(已刪減,只保留所需部分)
public class FileUtils { public static void writeFileUtf8(File file, String contents) throws IOException { writeFile(file, contents, "UTF-8"); } public static void writeFile(File file, String contents, String encoding) throws IOException { byte[] bytes = contents.getBytes(encoding); Files.write(file.toPath(), bytes, new OpenOption[]{StandardOpenOption.WRITE}); } private static byte[] read(InputStream source, int initialSize) throws IOException { int capacity = initialSize; byte[] buf = new byte[initialSize]; int nread = 0; while (true) { int n; while ((n = source.read(buf, nread, Math.min(capacity - nread, 4096))) > 0) { nread += n; } if (n < 0 || (n = source.read()) < 0) { return capacity == nread ? buf : Arrays.copyOf(buf, nread); } if (capacity <= 2147483639 - capacity) { capacity = Math.max(capacity << 1, 4096); } else { if (capacity == 2147483639) { throw new OutOfMemoryError("Required array size too large"); } capacity = 2147483639; } buf = Arrays.copyOf(buf, capacity); buf[nread++] = (byte) n; } } public static byte[] readAllBytes(Path path) throws IOException { SeekableByteChannel channel = Files.newByteChannel(path); Throwable var2 = null; byte[] var7; try { InputStream in = Channels.newInputStream(channel); Throwable var4 = null; try { long size = channel.size(); if (size > 2147483639L) { throw new OutOfMemoryError("Required array size too large"); } var7 = read(in, (int) size); } catch (Throwable var30) { var4 = var30; throw var30; } finally { if (in != null) { if (var4 != null) { try { in.close(); } catch (Throwable var29) { var4.addSuppressed(var29); } } else { in.close(); } } } } catch (Throwable var32) { var2 = var32; throw var32; } finally { if (channel != null) { if (var2 != null) { try { channel.close(); } catch (Throwable var28) { var2.addSuppressed(var28); } } else { channel.close(); } } } return var7; }}最后
以上就是動態(tài)編譯 Java 代碼以及生成 Jar 文件的方式。
感謝您的閱讀,如果喜歡本文歡迎關(guān)注和轉(zhuǎn)發(fā),本頭條號將堅持持續(xù)分享IT技術(shù)知識。對于文章內(nèi)容有其他想法或意見建議等,歡迎提出共同討論共同進(jìn)步。
總結(jié)
以上是生活随笔為你收集整理的eclipse编译java项目class文件_动态编译 Java 代码以及生成 Jar 文件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 128核CPU+864GB内存跑了157
- 下一篇: 女子网购iPhone 13 Pro竟是安