不多說直接上代碼:
動態編譯的主類:
package com.lkb.autoCode.util;
import com.lkb.autoCode.constant.AutoCodeConstant; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.tools.*; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * AutoCompiler 動態編譯器 * * @author Lilin * @date 2016/5/18 */ public class AutoCompiler { private static final Log log = LogFactory.getLog(AutoCompiler.class); /** * Description: Compile java source file to java class with run method * * @param fullFileName the java source file name with full path * @return true-compile successfully, false - compile unsuccessfully */ public static boolean compileFile(String fullFileName) { boolean bRet = false; // get compiler JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler(); // compile the java source code by run method int iCompileRet = oJavaCompiler.run(null, null, null, fullFileName); // set compile result if (0 == iCompileRet) { bRet = true; } return bRet; } /** * Description: Compile java source file to java class with getTask * method, it can specify the class output path and catch diagnostic * information * * @param fullFileName the java source file name with full path * @param outputPath the output path of java class file * @return true-compile successfully, false - compile unsuccessfully * @throws IOException */ public static boolean compileFile(String fullFileName, String outputPath) { System.out.println("源文件路徑:" + fullFileName); System.out.println("輸入路徑:" + outputPath); boolean bRet = false; // get compiler // TODO 當運行環境是JRE是無法直接ToolProvider.getSystemJavaCompiler();這么獲取 JavaCompiler oJavaCompiler = ToolProvider.getSystemJavaCompiler(); // define the diagnostic object, which will be used to save the diagnostic information DiagnosticCollector<JavaFileObject> oDiagnosticCollector = new DiagnosticCollector<JavaFileObject>(); // get StandardJavaFileManager object, and set the diagnostic for the object StandardJavaFileManager oStandardJavaFileManager = oJavaCompiler .getStandardFileManager(oDiagnosticCollector, null, null); // set class output location JavaFileManager.Location oLocation = StandardLocation.CLASS_OUTPUT; try { File outputFile = new File(outputPath); if (!outputFile.exists()) { outputFile.mkdir(); } List<File> dependencies = new ArrayList<File>(); // 加載依賴的jar文件 dependencies.addAll(getJarFiles(AutoCodeConstant.JARS_PATH)); // 加載依賴的class文件 dependencies.add(new File(AutoCodeConstant.BASE_PATH)); oStandardJavaFileManager.setLocation(StandardLocation.CLASS_PATH, dependencies); oStandardJavaFileManager.setLocation(oLocation, Arrays .asList(new File[]{outputFile})); File sourceFile = new File(fullFileName); // get JavaFileObject object, it will specify the java source file. Iterable<? extends JavaFileObject> oItJavaFileObject = oStandardJavaFileManager .getJavaFileObjectsFromFiles(Arrays.asList(sourceFile)); // -g 生成所有調試信息 // -g:none 不生成任何調試信息 // -g:{lines,vars,source} 只生成某些調試信息 // -nowarn 不生成任何警告 // -verbose 輸出有關編譯器正在執行的操作的消息 // -deprecation 輸出使用已過時的 API 的源位置 // -classpath <路徑> 指定查找用戶類文件的位置 // -cp <路徑> 指定查找用戶類文件的位置 // -sourcepath <路徑> 指定查找輸入源文件的位置 // -bootclasspath <路徑> 覆蓋引導類文件的位置 // -extdirs <目錄> 覆蓋安裝的擴展目錄的位置 // -endorseddirs <目錄> 覆蓋簽名的標准路徑的位置 // -d <目錄> 指定存放生成的類文件的位置 // -encoding <編碼> 指定源文件使用的字符編碼 // -source <版本> 提供與指定版本的源兼容性 // -target <版本> 生成特定 VM 版本的類文件 // -version 版本信息 // -help 輸出標准選項的提要 // -X 輸出非標准選項的提要 // -J<標志> 直接將 <標志> 傳遞給運行時系統 // 編譯選項,將編譯產生的類文件放在當前目錄下 //Iterable<String> options = Arrays.asList("-encoding", AutoCodeConstant.CONTENT_ENCODING, "-classpath", getJarFiles(AutoCodeConstant.JARS_PATH)); Iterable<String> options = Arrays.asList("-encoding", AutoCodeConstant.CONTENT_ENCODING); // compile the java source code by using CompilationTask's call method bRet = oJavaCompiler.getTask(null, oStandardJavaFileManager, oDiagnosticCollector, options, null, oItJavaFileObject).call(); //print the Diagnostic's information for (Diagnostic oDiagnostic : oDiagnosticCollector .getDiagnostics()) { // log.info("Error on line: " // + oDiagnostic.getLineNumber() + "; URI: " // + oDiagnostic.getSource().toString()); System.out.printf( "Code: %s%n" + "Kind: %s%n" + "Position: %s%n" + "Start Position: %s%n" + "End Position: %s%n" + "Source: %s%n" + "Message: %s%n", oDiagnostic.getCode(), oDiagnostic.getKind(), oDiagnostic.getPosition(), oDiagnostic.getStartPosition(), oDiagnostic.getEndPosition(), oDiagnostic.getSource(), oDiagnostic.getMessage(null)); } } catch (IOException e) { log.error("動態編譯出現異常", e); } finally { //close file manager if (null != oStandardJavaFileManager) { try { oStandardJavaFileManager.close(); } catch (IOException e) { e.printStackTrace(); log.error("動態編譯關閉文件管理器出現異常", e); } } } return bRet; } /** * 查找該目錄下的所有的jar文件 * * @param jarPath * @throws Exception */ private static List<File> getJarFiles(String jarPath) { final List<File> dependencyJars = new ArrayList<File>(); File sourceFile = new File(jarPath); if (sourceFile.exists()) {// 文件或者目錄必須存在 if (sourceFile.isDirectory()) {// 若file對象為目錄 // 得到該目錄下以.java結尾的文件或者目錄 File[] childrenFiles = sourceFile.listFiles(new FileFilter() { public boolean accept(File pathname) { if (pathname.isDirectory()) { return true; } else { String name = pathname.getName(); if (name.endsWith(".jar") ? true : false) { //jars[0] = jars[0] + pathname.getPath() + ";"; dependencyJars.add(pathname); return true; } return false; } } }); } } return dependencyJars; } }
輔助類:
package com.lkb.autoCode.constant;
/**
* AutoCodeConstant
*
* @author Lilin
* @date 2016/5/18
*/
public class AutoCodeConstant { /** * 登錄模板名稱 */ public static String LOGIN_TEMPLATE_FTL = "LoginClientTemplate.ftl"; /** * 項目根路徑 */ public final static String BASE_PATH = AutoCodeConstant.class.getResource("/").toString().replaceAll("^file:/", ""); /** * Jar包所在目錄 */ public final static String JARS_PATH = BASE_PATH.replace("classes","lib"); /** * 模板根路徑 */ public final static String BASE_TEMPLATE_PATH = BASE_PATH + "template"; /** * client目錄 */ public final static String LOCAL_CLIENT_BASE_PATH = "src/main/java/com/lkb/sb/client/"; /** * client目錄 */ public final static String CLIENT_BASE_PATH = "com/lkb/sb/client/"; /** * client類名后綴 */ public final static String LOGIN_CLIENT_SUFFIX = "LoginClient.java"; /** * 默認編譯輸出路徑 */ public final static String DEFULT_OUTPUT_PATH = "/"; /** * 編碼格式 */ public final static String CONTENT_ENCODING = "UTF-8"; }
開發背景:需求是根據代碼模板動態生成java代碼,並動態編譯
開發過程中遇到的阻塞:模板代碼中有依賴別的class文件和jar文件無法加載的問題
解決方法:
// 加載依賴的jar文件
dependencies.addAll(getJarFiles("jar文件的根路徑"));
// 加載依賴的class文件
dependencies.add(new File("class文件的根路徑")); /** * 查找該目錄下的所有的jar文件 * * @param jarPath * @throws Exception */ private static List<File> getJarFiles(String jarPath) { final List<File> dependencyJars = new ArrayList<File>(); File sourceFile = new File(jarPath); if (sourceFile.exists()) {// 文件或者目錄必須存在 if (sourceFile.isDirectory()) {// 若file對象為目錄 // 得到該目錄下以.java結尾的文件或者目錄 File[] childrenFiles = sourceFile.listFiles(new FileFilter() { public boolean accept(File pathname) { if (pathname.isDirectory()) { return true; } else { String name = pathname.getName(); if (name.endsWith(".jar") ? true : false) { //jars[0] = jars[0] + pathname.getPath() + ";"; dependencyJars.add(pathname); return true; } return false; } } }); } } return dependencyJars; }
