動態編譯運行java程序

動態編譯運行java程序

動態編譯java有兩種方式,一種是使用Runtime類執行cmd命令編譯java,詳見:https://blog.csdn.net/rico_zhou/article/details/79873344

另一種是java自身提供的方法,下面我們來實踐,如何通過JavaCompiler實現java代碼動態編譯。

首先獲取編譯器,注意tools.jar在jdk中不在jre中,詳見:https://blog.csdn.net/rico_zhou/article/details/80725086

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

獲取標準文件管理器實例

// 獲取標準文件管理器實例
		StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);

獲取要編譯的編譯單元

// 獲取要編譯的編譯單元
			Iterable<? extends JavaFileObject> compilationUnits = fileManager
					.getJavaFileObjectsFromFiles(sourceFileList);

 錯誤信息管理器

// 錯誤信息
		DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();

編譯選項

// 編譯選項,在編譯java文件時,編譯程序會自動的去尋找java文件引用的其他的java源文件或者class。
			// -sourcepath選項就是定義java源文件的查找目錄, -classpath選項就是定義class文件的查找目錄。
			Iterable<String> options = Arrays.asList(CMDConstant.CMD_COMMAND_JAVACOMPILE_1, encoding,CMDConstant.CMD_COMMAND_JAVACOMPILE_2, jars,CMDConstant.CMD_COMMAND_JAVACOMPILE_3, targetDir,CMDConstant.CMD_COMMAND_JAVACOMPILE_4, sourceDir);

編譯任務

CompilationTask compilationTask = compiler.getTask(null, fileManager, diagnostics, options, null,compilationUnits);

執行編譯

compilationTask.call();

完整代碼,源碼見github

package dynamicrunjava;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;

import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import constant.CMDConstant;
import constant.CodingConstant;
import constant.CommonSymbolicConstant;
import constant.ConfigConstant;
import constant.FileExtensionConstant;
import constant.OtherConstant;
import constant.SpiderConstant;

public class CompilerUtils {
	// 編譯,java文件目錄,jar包路徑,搜索資源路徑,編譯存放路徑,指定編譯器路徑
	public static boolean compilerJAVACodeFile(String filePath, String jarPath, String sourceDir, String targetDir,
			String javaCompilerPath) {
		// 注意路徑不同
		// 調用封裝好的編譯方法,需要提前準備好參數
		// 參數分別是:// 編碼格式,jar包,需要編譯的目錄,關聯查找目錄,編譯後存放目錄,錯誤信息
		// 編碼格式
		String encoding = CodingConstant.CODING_UTF_8;
		// jar包路徑拼接字符串
		String jars = CommonSymbolicConstant.EMPTY_STRING;
		// 開始挨個獲取
		// 獲取jar包拼接字符串
		try {
			jars = getJarFiles(jarPath);
		} catch (Exception e) {
			e.printStackTrace();
			jars = CommonSymbolicConstant.EMPTY_STRING;
		}
		// 錯誤信息
		DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
		// 開始編譯
		try {
			if (!compiler(encoding, jars, filePath, filePath, targetDir, diagnostics, javaCompilerPath)) {
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

		return true;
	}

	// 查找jar
	public static String getJarFiles(String jarPath) throws Exception {
		String jars = CommonSymbolicConstant.EMPTY_STRING;
		File sourceFile = new File(jarPath);
		if (sourceFile.exists()) {
			if (sourceFile.isDirectory()) {
				// 得到該目錄下以.jar結尾的文件或者目錄
				File[] childrenFiles = sourceFile.listFiles();
				jars = filterJarFile(childrenFiles);
			}
		}
		return jars;
	}

	// 過濾文件
	private static String filterJarFile(File[] childrenFiles) {
		String jars = CommonSymbolicConstant.EMPTY_STRING;
		for (File pathname : childrenFiles) {
			if (!pathname.isDirectory()) {
				String name = pathname.getName();
				if (name.endsWith(FileExtensionConstant.FILE_EXTENSION_POINT_CODEFILE_JAR) ? true : false) {
					jars = jars + pathname.getPath() + CommonSymbolicConstant.SEMICOLON;
				}
			}
		}
		return jars;
	}

	// 編碼格式,jar包,需要編譯的目錄,關聯查找目錄,編譯後存放目錄,錯誤信息
	public static boolean compiler(String encoding, String jars, String filePath, String sourceDir, String targetDir,
			DiagnosticCollector<JavaFileObject> diagnostics, String javaCompilerPath) throws Exception {
		// 獲取編譯器實例
		// JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		// 此處無法加載如果沒有安裝jdk,另爲了安全起見需要使用單獨的jre
		// 解決方法一,通過手動加載tools.jar獲取相關類方法獲取編譯器再編譯
		// 解決方法二,通過Runtime方法執行cmd命令設置Javahome
		JavaCompiler compiler = null;
		try {
			// 獲取指定的編譯器(需要tools.jar)
			File file = new File(javaCompilerPath == null ? "" : javaCompilerPath);
			if (!file.exists()) {
				// 獲取系統編譯器需要把jdk中的tool.jar複製到jre中,因爲自帶的jre沒有
				// compiler = ToolProvider.getSystemJavaCompiler();
				// 獲取系統變量
				String javahome = System.getenv(ConfigConstant.CONFIG_ENV_JAVA_HOME);
				file = new File(javahome + File.separator + ConfigConstant.CONFIG_DEFAULT_TOOLS_PATH);
				if (!file.exists()) {
					return false;
				}
			}
			compiler = getJavaCompilerByLocation(file);
		} catch (Exception e1) {
			e1.printStackTrace();
			return false;
		}
		// 獲取標準文件管理器實例
		StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
		try {
			// 得到filePath目錄下的所有java源文件
			File sourceFile = new File(filePath);
			List<File> sourceFileList = new ArrayList<File>();
			// 根據文件判斷是否是java文件,並存放List
			getSourceFiles(sourceFile, sourceFileList);
			// 沒有java文件,直接返回
			if (sourceFileList.size() == 0) {
				return false;
			}
			// 獲取要編譯的編譯單元
			Iterable<? extends JavaFileObject> compilationUnits = fileManager
					.getJavaFileObjectsFromFiles(sourceFileList);
			// 編譯選項,在編譯java文件時,編譯程序會自動的去尋找java文件引用的其他的java源文件或者class。
			// -sourcepath選項就是定義java源文件的查找目錄, -classpath選項就是定義class文件的查找目錄。
			Iterable<String> options = Arrays.asList(CMDConstant.CMD_COMMAND_JAVACOMPILE_1, encoding,
					CMDConstant.CMD_COMMAND_JAVACOMPILE_2, jars, CMDConstant.CMD_COMMAND_JAVACOMPILE_3, targetDir,
					CMDConstant.CMD_COMMAND_JAVACOMPILE_4, sourceDir);
			CompilationTask compilationTask = compiler.getTask(null, fileManager, diagnostics, options, null,
					compilationUnits);
			// 運行編譯任務
			return compilationTask.call();
		} finally {
			fileManager.close();
		}
	}

	// 獲取非系統編譯器
	public static JavaCompiler getJavaCompilerByLocation(File f1) throws Exception {
		// tool.jar我放在了本項目根目錄下,可自行更改
		String p = f1.getAbsolutePath();
		URL[] urls = new URL[] { new URL(OtherConstant.OTHER_URL_1 + p) };
		URLClassLoader myClassLoader1 = new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
		Class<?> myClass1 = myClassLoader1.loadClass(OtherConstant.OTHER_JAVACTOOL_CLASS);
		JavaCompiler compiler = myClass1.asSubclass(JavaCompiler.class).asSubclass(JavaCompiler.class).newInstance();
		return compiler;
	}

	// 判斷不爲空
	public static boolean isnull(String str) {
		if (null == str) {
			return false;
		} else if (CommonSymbolicConstant.EMPTY_STRING.equals(str)) {
			return false;
		} else if (str.equals(CommonSymbolicConstant.NULL)) {
			return false;
		} else {
			return true;
		}
	}

	// 遞歸查找java文件
	private static void getSourceFiles(File sourceFile, List<File> sourceFileList) throws Exception {
		if (sourceFile.exists() && sourceFileList != null) {
			if (sourceFile.isDirectory()) {
				// 得到該目錄下以.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(FileExtensionConstant.FILE_EXTENSION_POINT_CODEFILE_JAVA) ? true
									: false) {
								return true;
							}
							return false;
						}
					}
				});
				// 遞歸調用
				for (File childFile : childrenFiles) {
					getSourceFiles(childFile, sourceFileList);
				}
			} else {// 若file對象爲文件
				sourceFileList.add(sourceFile);
			}
		}
	}

}

如何動態執行java代碼,見:

https://blog.csdn.net/rico_zhou/article/details/79873344 和 https://blog.csdn.net/rico_zhou/article/details/81301284

https://blog.csdn.net/rico_zhou/article/details/81284048

github源碼:https://github.com/ricozhou/dynamiccompilerjava

 

發佈了56 篇原創文章 · 獲贊 42 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章