自定義類載入如何打破雙親委託機制的正確姿勢
阿新 • • 發佈:2019-01-24
通過自定義類載入打破類載入雙親規則
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(name);
try{
if(clazz == null) clazz = findClass(name);
}catch (ClassNotFoundException e){
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if(clazz == null && getParent() != null){
clazz = getParent().loadClass(name);
}
if(resolve){
resolveClass(clazz);
}
return clazz;
}
其中clazz = findClass(name);必須catch,儘管loadClass也丟擲了ClassNotFoundException異常,因為我們現在要實現的是,自定義類載入器載入不到(也就是找不到這個類),就讓自定義載入器的父載入器(ApplicationClassLoader)去載入,如果你這裡將異常丟擲了,父載入器根本沒有機會去載入。那麼什麼時候自定載入器載入不到呢,這個問題就在findClass(name)裡,看你是怎麼實現的findClass了。如:
/**
* 這是查詢非classpath下的類(自定義目錄),並未打破雙親規則
* @param name
* @return
* @throws ClassNotFoundException
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// String className = name.substring(name.lastIndexOf("."));
// name.substring(0, name.lastIndexOf("."));
String classPath = dir + File.separator + name.replace(".", "\\") + ".class";
// File classFile = new File(dir, classPath);
File classFile = new File(classPath);
if(!classFile.exists()){
throw new ClassNotFoundException("this class "+ classPath +" is not found under [" + dir + "]");
}
byte[] classBytes = loadClassBytes(classFile);//通過class檔案獲取陣列
if(null == classBytes || classBytes.length == 0)
throw new ClassNotFoundException("load this class is failed");
//這樣還是載入不到java.*的類,因為在ClassLoader裡面是寫死的
Class c = AccessController.doPrivileged(new PrivilegedAction<Class>() {
@Override
public Class run() {
return defineClass(name, classBytes, 0, classBytes.length);
}
});
System.out.println("find class successfully");
return c;
}
如上,在自定義目錄(非classpath)下找不到這個你這個類名指定的class檔案,就會丟擲ClassNotFoundException異常,所以像java.*包裡的類,自定義載入器是載入不到的(就算有這個檔案,也載入不到),所以這時就需要父載入器去載入了,ClassNotFoundException也由父載入器去丟擲。注意從父載入器開始,還是滿足雙親委託機制的。
最後說明一下,其實tomcat類載入機制並未打破類雙親委託機制,它只是沒有classpath的概念,然後載入的類是從自定義目錄裡去載入的,所以才會去實現自定義載入器。