今天偶然看到一篇Java技術(shù)棧發(fā)表的文章垃环,關(guān)于自定義類加載器的實(shí)現(xiàn)感覺(jué)有點(diǎn)問(wèn)題。在此做一個(gè)梳理仅颇。
原文鏈接:http://www.reibang.com/p/e808ed28a5d6
本文代碼示例來(lái)自原文胁塞,稍作修改。
我們看看原文的例子:
image.png
疑問(wèn):第一時(shí)間感覺(jué)兩處“return super.loadClass(name);”應(yīng)該直接“return null”乔煞,不然在找不到類的情況下會(huì)死循環(huán)?!
接下來(lái)做了一番分析和驗(yàn)證。
自定義加載器使用中我們調(diào)用ClassLoader.loadClass加載一個(gè)類柒室,來(lái)loadClass關(guān)于類加載雙親委托模型的實(shí)現(xiàn):
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//判斷當(dāng)前類是否已經(jīng)被加載渡贾,調(diào)用本地方法findLoadedClass0
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
//雙親委托模型:父加載器不為空,調(diào)用父加載器加載雄右,否則查看啟動(dòng)類加載器是否已加載該類
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//調(diào)用本地方法findBootstrapClass
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//父加載器或者啟動(dòng)類加載器都未加載該類空骚,則調(diào)用本加載器的findClass方法
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
//是否鏈接(驗(yàn)證锦溪、準(zhǔn)備、解析)
if (resolve) {
resolveClass(c);
}
return c;
}
}
findClass在ClassLoader中并未實(shí)現(xiàn)府怯,須用戶在自定義加載器中實(shí)現(xiàn):
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
圖解雙親委托模型:
image.png
一句話總結(jié):自底向上委托,自頂向下加載防楷。
我們?cè)倩剡^(guò)頭來(lái)分析找不到類的情況下代碼的調(diào)用過(guò)程:
LocalClassLoader.loadClass => 父加載器返回null => LocalClassLoader.findClass失敗 => super.loadClass === LocalClassLoader.loadClass
代碼進(jìn)入死循環(huán)
本地驗(yàn)證:
public class LocalClassLoader extends ClassLoader {
private String path = "F:/study/";
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> cls = findLoadedClass(name);
if (cls != null) {
return cls;
}
if (!name.endsWith(".Key")) {
return super.loadClass(name);
}
try {
InputStream is = new FileInputStream(path + name.replace(".", "/") + ".class");
byte[] bytes = IOUtils.readNBytes(is, is.available());
return defineClass(name, bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
return super.loadClass(name);
}
public static void main(String[] args) {
try {
LocalClassLoader lcl = new LocalClassLoader();
Class<?> cls = lcl.loadClass("com.yalin.test.Key"); //1
//Class<?> cls = lcl.loadClass("com.yalin.test.jdk.Key"); //2
Field field = cls.getDeclaredField("key");
field.setAccessible(true);
Object value = field.get(cls.newInstance());
System.out.println(value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
加載F:/study/目錄存在的類com.yalin.test.Key牺丙,輸出成功:
image.png
加載F:/study/目錄不存在的類com.yalin.test.jdk.Key,代碼進(jìn)入死循環(huán):
image.png
轉(zhuǎn)載請(qǐng)備注原文鏈接复局。