Java動態編譯和動態載入詳解
一.動態編譯
在某些情況下,我們需要動態生成java程式碼,通過動態編譯,然後執行程式碼。JAVA API提供了相應的工具(JavaCompiler)來實現動態編譯。
//獲取JavaCompiler JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
//獲取java檔案管理類 StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, null);
//獲取java檔案物件迭代器 Iterable<? extends JavaFileObject> it = manager.getJavaFileObjects(files);
//設定編譯引數 ArrayList<String> ops = new ArrayList<String>(); ops.add("-Xlint:unchecked");
//可以指定原始檔或目標檔案的JDK版本 ops.add("-source")/ops.add("-target");; ops.add("1.5");
//獲取編譯任務:第一個null,用於輸出錯誤的流,預設是System.err;第二個null,diagnosticListener: 編譯器的預設行為 ;第三個null,classes,參與編譯的class,為null時表示全部
//JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, ops, null, it);
//執行編譯任務 task.call();
動態編譯幾個關鍵類
類名 |
作用 |
建立方式 |
JavaCompiler |
動態編譯的入口,也是基礎類 |
ToolProvider.getSystemJavaCompiler() |
StandardJavaFileManager |
java檔案管理類 |
compiler.getStandardFileManager |
Iterable |
檔案物件迭代器 |
manager.getJavaFileObjects(files) |
ArrayList<String> |
編譯引數 |
new ArrayList<String>() |
踩坑記
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 這句程式碼在IDEA中執行正常,然後打成jar包在桌面雙擊執行時得到的是null
問題解決:
檢視ToolProvider原始碼可知,是因為找不到tools.jar---》將tools.jar複製到jre/lib下
二.動態載入
JAVA中的類載入:雙親委派模式
URL url = new URL("file:" + CLASS_PATH);
ClassLoader classLoader = new URLClassLoader(new URL[]{url});
Class<?> cls = classLoader.loadClass(name);
Method method = cls.getDeclaredMethod(String methodName)
Object obj = cls.newInstance();
Object result = method.invoke(Object obj, Object[] params);
class.forName()和classLoader區別:
//Class.forName(String className) 這是1.8的原始碼
public static Class<?> forName(String className) throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
//注意第二個引數,是指Class被loading後是不是必須被初始化。 不初始化就是不執行static的程式碼即靜態程式碼 class.forName()前者除了將類的.class檔案載入到jvm中之外,還會對類進行解釋,執行類中的static塊。
而classLoader只幹一件事情,就是將.class檔案載入到jvm中。
擴充套件:TOMCAT中的類載入架構
1、JAVA_HOME:設定為JDK的安裝目錄,如D:\java\jdk1.8。一些依賴JAVA的軟體通過該環境變數來獲得JDK的安裝目錄,如大家都知道的Eclipse、Tomcat等。
2、PATH:在已有值最後加上 ;%JAVA_HOME%\bin ,其中;是分隔符,用於分開每個目錄,其中%JAVA_HOME%則是引用上面的JAVA_HOME環境變數的值,可以直接用JDK的安裝目錄代替,下面的CLASSPATH類似。執行路徑(也就是PATH環境變數的值)是隻作業系統搜尋本地可執行檔案的目錄列表,在shell中執行命令時,它會在執行路徑中查詢所對應的程式。所以在PATH中加入了%JAVA_HOME%\bin目錄後,在shell中就可以執行javac或者java命令。
3、CLASSPATH:設定值為.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; 注意其中的標點符號,其中第一個點“.”表示當前目錄,而分號“;”則是分隔符。 這個CLASSPATH為JVM載入類的目錄,比如HelloWorld.java中有import其他類,用javac編譯時它會在CLASSPATH中去找你所import的類,如果找不到則出現編譯錯誤。 另外在使用java時使用-classpath 引數可以指定要載入類的目錄,比如命令列執行:C:/UsersJakey>java -classpath D:\temp HelloWorld ,這樣就可以當命令列在C盤時執行D盤目錄中的JAVA類。