Java類檔案動態編譯並執行方法
阿新 • • 發佈:2019-01-09
package com.example.demo.dimension; import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.Stack; import javax.tools.JavaCompiler; import javax.tools.JavaCompiler.CompilationTask; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; /** * @ClassName: CompilerTest * @Description: JAVA原始檔動態編譯 * @date: 2018年7月16日 上午9:45:32 * */ public class CompilerTest { public static void main(String[] args) throws Exception { String classPath = "G:/deleted/apktool/dynamicClass" ; System.out.println( calculate("1.0+1.0",classPath)); } /** * @Title: testHello * @Description: 測試動態編譯java檔案 * @param: @param classPath * @return: void * @throws */ public static void testHello(String classPath){ String className = "Main" ; String source = "public class "+className+" { public static void main(String[] args) {System.out.println(\"Hello World!\");} }"; boolean isSuccess = complie(source,className,classPath) ; System.out.println( "Complie successfully:"+isSuccess ); } /** * @Title: calculate * @Description: 測試動態編譯並且載入類並執行對應方法 * @param: @param expr * @param: @param classPath * @param: @return * @param: @throws Exception * @return: double * @throws */ private static double calculate(String expr,String classPath) throws Exception { String className = "Main"; String methodName = "calculate"; String source = "public class " + className + " { public static double " + methodName + "() { return " + expr + "; } }"; // 省略動態編譯Java原始碼的相關程式碼,參見上一節 boolean result = complie(source,className, classPath); if (result) { loadClass( new File(classPath) ); ClassLoader loader = CompilerTest.class.getClassLoader(); try { Class<?> clazz = loader.loadClass(className); Method method = clazz.getMethod(methodName, new Class<?>[] {}); Object value = method.invoke(null, new Object[] {}); return (Double) value; } catch (Exception e) { throw new Exception("內部錯誤。",e); } } else { throw new Exception("錯誤的表示式。"); } } /** * @Title: complie * @Description: 動態編譯java類成成class檔案放入指定目錄 * @param: @param expr * @param: @param classPath * @param: @return * @param: @throws Exception * @throws */ public static boolean complie(String source,String className,String classPath){ JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); StringSourceJavaObject sourceObject = null; try { Iterable<String> options = Arrays.asList("-d", classPath); sourceObject = new CompilerTest.StringSourceJavaObject(className, source); Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(sourceObject); CompilationTask task = compiler.getTask(null, fileManager, null, options, null, fileObjects); boolean result = task.call(); return result ; } catch (URISyntaxException e) { e.printStackTrace(); } return false ; } private static class StringSourceJavaObject extends SimpleJavaFileObject { private String content = null; public StringSourceJavaObject(String name, String content) throws URISyntaxException { super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); this.content = content; } public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return content; } } /** * @Title: loadClass * @Description: 動態載入class檔案 * @param: @param clazzPath * @param: @throws Exception * @return: void * @throws */ public static void loadClass(File clazzPath) throws Exception{ // 設定class檔案所在根路徑 // 例如/usr/java/classes下有一個test.App類,則/usr/java/classes即這個類的根路徑,而.class檔案的實際位置是/usr/java/classes/test/App.class // File clazzPath = new File(class檔案所在根路徑); // 記錄載入.class檔案的數量 int clazzCount = 0; //only handle the folder if( clazzPath.isFile() ){ clazzPath = clazzPath.getParentFile() ; } if (clazzPath.exists() && clazzPath.isDirectory()) { // 獲取路徑長度 int clazzPathLen = clazzPath.getAbsolutePath().length() + 1; Stack<File> stack = new Stack<>(); stack.push(clazzPath); // 遍歷類路徑 while (stack.isEmpty() == false) { File path = stack.pop(); File[] classFiles = path.listFiles(new FileFilter() { public boolean accept(File pathname) { return pathname.isDirectory() || pathname.getName().endsWith(".class"); } }); for (File subFile : classFiles) { if (subFile.isDirectory()) { stack.push(subFile); } else { if (clazzCount++ == 0) { Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); boolean accessible = method.isAccessible(); try { if (accessible == false) { method.setAccessible(true); } // 設定類載入器 URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); // 將當前類路徑加入到類載入器中 method.invoke(classLoader, clazzPath.toURI().toURL()); } finally { method.setAccessible(accessible); } } // 檔名稱 String className = subFile.getAbsolutePath(); className = className.substring(clazzPathLen, className.length() - 6); className = className.replace(File.separatorChar, '.'); // 載入Class類 Class.forName(className); System.out.println( String.format( "讀取應用程式類檔案[class=%s]", className) ); } } } } } }
參考:
動態載入jar和class檔案 https://blog.csdn.net/mousebaby808/article/details/31788325
JAVA原始檔動態編譯 https://blog.csdn.net/m1993619/article/details/78734682