一般情況下對java源文件的編譯均是在代碼完成后使用javac編譯的,不管是使用 IDE還是直接使用命令行。這里要說的情況是比較特別的,就是在代碼內動態(tài)的編譯一些代碼。比如你想通過在某個目錄下通過放置一些源代碼的方式來實現(xiàn)對程序功能的動態(tài)擴展,那么你的程序就需要具有一種對源代碼的編譯、加載、運行的能力,可能就需要本文介紹的3種方法。 方法1:通過調用本機的javac命令來編譯。 在java程序中調用javac命令可以通過調用Runtime類的exec或是ProcessBuilder類的start方法來完成,這兩個類的功能基本相同,用法也比較相似,這里的例子我們就用ProcessBuilder來演示。如果是JDK1.5之前的版本請使用Runtime類完成相同的功能。 開始之前先來點準備工作,將下面的類放到 c:\mytest\src\ 目錄下,這個類我們不會在IDE中編譯,而是由我們程序完成其編譯。保存時使用UTF-8格式??梢灾苯釉诟郊邢螺d這個類。 Java代碼 [img]http://devbbs./attachments/images/302-icon_copy.gif[/img] public class HelloWorld { public void sayHello(String in) { System.out.println("動態(tài)編譯成功"); System.out.println("使用編譯方式:" + in); } } public class HelloWorld { public void sayHello(String in) { System.out.println("動態(tài)編譯成功"); System.out.println("使用編譯方式:" + in); } } 準備工作完成,下面就看一下我們程序的代碼,這里只列出主要代碼 Java代碼 [img]http://devbbs./attachments/images/302-icon_copy.gif[/img] public class JavacCompile { private static String filePath = "c:\\mytest\\src\\HelloWorld.java"; private static String binDir = "c:\\mytest\\bin"; public static void main(String[] args) { File binOutDir = new File(binDir); if (!binOutDir.exists()) { binOutDir.mkdirs(); } // 設置javac的編譯參數(shù),使用-encoding指定編碼方式,-d并指定編譯生成class文件目錄 ProcessBuilder pb = new ProcessBuilder("javac","-encoding", "UTF-8","-d", binDir, filePath); try { // 開始調用javac命令編譯 final Process proc = pb.start(); // 處理進程的輸出,避免掛死 new Thread(new Runnable() { public void run() { processStream(proc.getInputStream()); processStream(proc.getErrorStream()); } }).start(); // 等待編譯完成 proc.waitFor(); // 加載編譯好的類,并調用相應的方法 new LoaderClassByDir(binDir).execSayHello("javac"); } catch (Exception ex) { Logger.getLogger(JavacCompile.class.getName()).log(Level.SEVERE, null, ex); } } private static void processStream(InputStream stderr) { ... } } public class JavacCompile { private static String filePath = "c:\\mytest\\src\\HelloWorld.java"; private static String binDir = "c:\\mytest\\bin"; public static void main(String[] args) { File binOutDir = new File(binDir); if (!binOutDir.exists()) { binOutDir.mkdirs(); } // 設置javac的編譯參數(shù),使用-encoding指定編碼方式,-d并指定編譯生成class文件目錄 ProcessBuilder pb = new ProcessBuilder("javac","-encoding", "UTF-8","-d", binDir, filePath); try { // 開始調用javac命令編譯 final Process proc = pb.start(); // 處理進程的輸出,避免掛死 new Thread(new Runnable() { public void run() { processStream(proc.getInputStream()); processStream(proc.getErrorStream()); } }).start(); // 等待編譯完成 proc.waitFor(); // 加載編譯好的類,并調用相應的方法 new LoaderClassByDir(binDir).execSayHello("javac"); } catch (Exception ex) { Logger.getLogger(JavacCompile.class.getName()).log(Level.SEVERE, null, ex); } } private static void processStream(InputStream stderr) { ... } } LoaderClassByDir類的代碼會保含在后面的上傳的文件中,因為這里主要介紹完成程序中對java源文件的編譯,對于類的加載和運行不多做描述,可以參考LoaderClassByDir類中的簡單實現(xiàn)。 方法2:使用Sun的tools.jar包時的com.sun.tools.javac.Main類完成對代碼的編譯。 注意這個類的是在tools.jar包里,tools.jar不是標準的Java庫,在使用時必須要設置這個jar的路徑,使用IDE時需要顯示的引入到編譯路徑中,不然會找不到。我們使用此類改寫上面的編譯類如下: Java代碼 [img]http://devbbs./attachments/images/302-icon_copy.gif[/img] public class JavacCompile { private static String filePath = "c:\\mytest\\src\\HelloWorld.java"; private static String binDir = "c:\\mytest\\bin"; public static void main(String[] args) { File binOutDir = new File(binDir); if (!binOutDir.exists()) { binOutDir.mkdirs(); } // 將編譯參數(shù)通過數(shù)組傳遞到編譯方法中,該函數(shù)的方法和javac的參數(shù)完成一致 Main.compile(new String[]{"-encoding", "UTF-8","-d", binDir, filePath}); try { // 加載編譯好的類,并調用相應的方法 new LoaderClassByDir(binDir).execSayHello("sun tools"); } catch (Exception ex) { Logger.getLogger(JavacCompile.class.getName()).log(Level.SEVERE, null, ex); } } } public class JavacCompile { private static String filePath = "c:\\mytest\\src\\HelloWorld.java"; private static String binDir = "c:\\mytest\\bin"; public static void main(String[] args) { File binOutDir = new File(binDir); if (!binOutDir.exists()) { binOutDir.mkdirs(); } // 將編譯參數(shù)通過數(shù)組傳遞到編譯方法中,該函數(shù)的方法和javac的參數(shù)完成一致 Main.compile(new String[]{"-encoding", "UTF-8","-d", binDir, filePath}); try { // 加載編譯好的類,并調用相應的方法 new LoaderClassByDir(binDir).execSayHello("sun tools"); } catch (Exception ex) { Logger.getLogger(JavacCompile.class.getName()).log(Level.SEVERE, null, ex); } } } 使用這個類后,同樣的功能代碼變得更加簡潔。 方法3:使用javax.tools包 從上面可以看到方法2的缺點就是tools.jar需要我們自行導入。而在Java SE6中為我們提供了標準的包來操作Java編譯器,這就是javax.tools包。使用這個包,我們可以不用將jar文件路徑添加到 classpath中了。 使用這個類的方法和上面的類很相似,我只需要將 Java代碼 [img]http://devbbs./attachments/images/302-icon_copy.gif[/img] Main.compile(new String[]{"-encoding", "UTF-8","-d", binDir, filePath}); Main.compile(new String[]{"-encoding", "UTF-8","-d", binDir, filePath}); 替換成: Java代碼 [img]http://devbbs./attachments/images/302-icon_copy.gif[/img] // 將編譯參數(shù)通過數(shù)組傳遞到編譯方法中,該函數(shù)的方法和javac的參數(shù)完成一致 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); compiler.run(null, null, null, "-encoding", "UTF-8","-d", binDir, filePath); // 將編譯參數(shù)通過數(shù)組傳遞到編譯方法中,該函數(shù)的方法和javac的參數(shù)完成一致 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); compiler.run(null, null, null, "-encoding", "UTF-8","-d", binDir, filePath); 就可以完成相應的編譯功能,這里簡介一下run的使用方法: 注意:使用上傳文件中的代碼做測試時,為避免上次編譯的影響記得手動刪除C:\mytest\bin下的類文件 |
|