java ClassLoader機制和如何載入外部class檔案(含程式碼)
生命週期有:載入(Loading)--》驗證(Verification)--》準備(Preparation)---》解析(Resolution)--》初始化(Initiation)---》使用(Using)----》解除安裝(Unloading)。
其中標黃的驗證---》準備---》解析被稱為連線(Linking)。
就程式碼執行而言:
1.連線階段:不執行程式設計師程式碼
2.載入階段:可以重寫ClassLoader來執行我們的程式碼
3.連線後階段:執行該類的程式碼
ClassLoader:
ClassLoader是為了將class檔案的byte陣列,主要的方法是findClass用於查詢類。
Java7有個迅速將File程式設計byte[]的方法:
byte[] cLassBytes = null;
Path path;
try {
path = Paths.get(new URI("file:///D:/MyScript/TestClassLoader.class"));
cLassBytes = Files.readAllBytes(path);
每個ClassLoader物件都有一個ClassLoader型別的欄位,暫且成為parent。當呼叫本類的loadClass方法載入類時,會先呼叫parent的ClassLoader,如果他沒找到,才會呼叫本類的findClass方法去查詢類。(這樣,應用的Launcher$AppClassLoader也可以通過BootStrapClassLoader來呼叫String類了)。
查詢類的順序,與對應的ClassLoader
系統查詢類的順序是:
1).Bootstrap classes: the runtime classes in rt.jar, internationalization classes in i18n.jar, and others.
2).Installed extensions: classes in JAR files in the lib/ext directory of the JRE, and in the system-wide, platform-specific extension directory (such as /usr/jdk/packages/lib/ext
3).The class path: classes, including classes in JAR
files, on paths specified by the system property java.class.path. If a JAR file on the class
path has a manifest with the Class-Path
attribute,
JAR files specified by the Class-Path
attribute
will be searched also. By default, thejava.class.path
property's
value is .
, the current directory. You can
change the value by using the -classpath or -cp command-line
options, or setting the CLASSPATH
environment
variable. The command-line options override the setting of the CLASSPATH
environment
variable.
中文就是:
1)rt.jar等(這個jar是什麼呢?你用java -verbose 類名 執行下就看到了:Loaded java.io.File from C:\Program Files\Java\jre1.8.0_31\lib\rt.jar,也就是經常呼叫的String、Long等類,屬於SDK中的)(用BootStrapClassLoader載入)
2)SDK中的拓展類,包名為javax的(java和javax都是Java的API(Application Programming Interface)包,java是核心包,javax的x是extension的意思);
(用Launcher$ExtClassLoader載入)
3)系統環境變數CLASSPATH的路徑,當前目錄(或者用命令列帶 -classpath重新)
(用Launcher$AppClassLoader載入)
可是我的類檔案在D:\MyScript\TestClassLoader.class不在上述三種。
2:解決方案
1.場景:我想載入一個d盤上的類,路徑是:D:\MyScript\TestClassLoader.class,其不在上述三種情況。
通過重寫一個ClassLoader進行載入,不過有現成的URLClassLoader我們就不要重造輪子了。
自定義ClassLoader程式碼
public class MyClassLoader extends ClassLoader{
@Override
protected Class<?> findClass(String className)
throws ClassNotFoundException {
byte[] cLassBytes = null;
//Java 7有下列API
<span style="white-space:pre"> </span>Path path;
try {
path = Paths.get(new URI("file:///D:/MyScript/TestClassLoader.class"));
cLassBytes = Files.readAllBytes(path);
} catch (IOException | URISyntaxException e) {
e.printStackTrace();
}
Class cLass = defineClass(cLassBytes, 0, cLassBytes.length);
return cLass;
}
}
使用的時候:
Class.forName("TestClassLoader", true, new MyClassLoader());
用URLClassLoader來簡化
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// This URL for a directory will be searched *recursively*
URL classes;
try {
classes = new URL( "file:///D:/MyScript/" );
ClassLoader custom =
new URLClassLoader( new URL[] { classes }, systemClassLoader );
// this class should be loaded from your directory
Class< ? > clazz = custom.loadClass( "TestClassLoader" );
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
URLClassLoader用的URL只能接受目錄和jar包:
結尾: /代表該目錄下的來; 非/而是檔案則預設為jar包。
貼一個jar包的程式碼:
public URL findResource(String name) {
try {
File file = new File(jarFile);
String url = file.toURL().toString();
return new URL("jar:"+url+"!/"+name);
} catch (Exception e) {
return null;
}
}<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: rgb(255, 255, 255);"> </span>
另外我用反射來驗證類是否載入成功了,clazz.newInstance()也可以生成相關程式碼。