// 待類加載的類
package com.infuq;
public class Ticket { }
自定義類加載器 MyClassLoader
package com.infuq;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* 自定義類加載器
*
* 加載指定路徑scanPath下的類文件
*
*/
public class MyClassLoader extends ClassLoader {
// MyClassLoader可以掃描的路徑. 例如: /usr/include
private final String scanPath ;
public MyClassLoader(String scanPath){
super();
this.scanPath = scanPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = readFileData(name);
if (classData != null) {
return this.defineClass(name, classData, 0, classData.length);
}
return null;
}
// 讀取clazzName對應(yīng)的文件內(nèi)容, 返回字節(jié)數(shù)組
private byte[] readFileData(String clazzName) {
File file;
/*
* 假如scanPath=/usr/include
* 由于入?yún)lazzName是類的全限定名, 例如java.lang.Object
* 為了讀取clazzName對應(yīng)的文件, 需要將字符串`java.lang.Object`轉(zhuǎn)成`java/lang/Object`
* 最后得到完整文件路徑/usr/include/java/lang/Object.class
*/
if (System.getProperty("os.name").contains("Windows")) {
clazzName = clazzName.replace(".", "\\");
file = new File(scanPath + "\\" + clazzName + ".class");
}
else {
clazzName = clazzName.replace(".", "/");
file = new File(scanPath + "/" + clazzName + ".class");
}
// 讀取
if (file.exists()) {
FileInputStream in = null;
ByteArrayOutputStream out;
try {
in = new FileInputStream(file);
out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int size;
while ((size = in.read(buffer)) != -1) {
out.write(buffer, 0, size);
}
return out.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
測試類 Example
package com.infuq;
import java.util.Scanner;
public class Example {
public static void main(String[] args) throws Exception {
System.out.print("請輸入類加載的路徑: ");
Scanner scanner = new Scanner(System.in);
/*
* 從命令行讀取指定掃描的路徑
* 例如
* Unix : /usr/bin/
* Windows : D:\\repository\\class
*/
String scanPath = scanner.next();
MyClassLoader myClassLoader = new MyClassLoader(scanPath);
/*
* 讀取類的全限定名, 例如 java.lang.Object
*
*/
System.out.print("請輸入待加載的類全限定名稱: ");
String packageNamePath = scanner.next();
System.out.println();
Class<?> clazz = myClassLoader.loadClass(packageNamePath);
if (clazz != null)
System.out.println(packageNamePath + "類加載器是: " + clazz.getClassLoader());
System.out.println("系統(tǒng)類加載器掃描文件路徑: " + System.getProperty("java.class.path"));
}
}
編譯 Ticket.java , MyClassLoader.java , Example.java 三個(gè)源文件, 文件結(jié)構(gòu)圖如下
以上源文件在 /Users/infuq/Repository/GitLab/infuq-others/Lab/2022-3-22 目錄下
其中還把待加載的Ticket.class文件拷貝一份到了自定義的JAVA-INF目錄下, 后面會(huì)用到
【測試一】
本計(jì)劃讓自定義類加載器MyClassLoader 加載/Users/infuq/Repository/GitLab/infuq-others/Lab/2022-3-22/JAVA-INF目錄下的com.infuq.Ticket類, 根據(jù)雙親委派, 委派給了系統(tǒng)類加載器. 而當(dāng)前目錄下就有一個(gè)com.infuq.Ticket類, 因此這個(gè)Ticket類被系統(tǒng)類加載器加載了.
改個(gè)名稱, 故意讓系統(tǒng)類加載器找不到Ticket類
繼續(xù)上面的相同執(zhí)行
這個(gè)時(shí)候com.infuq.Ticket就被自定義類加載器加載了.