如何快速寫一個違背雙親委託機制的classloader
很多情況下,不得以必須寫個classloader來滿足需求。例如你一個工程裡你想用相同的資料庫的多個版本,自己制定了一個jar包目錄,沒有classloader管理等等。如果是一個遵循java已經規定好的機制的classloader(雙親委託以及載入依賴類的classloader繼續載入剩下的類)。直接繼承classloader就可以。(一般選擇urlclassloder,他幫你實現了不少功能)。但是往往有需求需要載入兩個相同的Jar包,例如web應用中的webappclassloader。不同的應用的相同war包都需要被同級的載入,優先順序高於was的lib目錄。
編寫classloader
加入快取
classloader的本質是載入位元組碼到記憶體,而且相同位元組碼不能被重複載入,所以我們需要加入快取,簡單選擇一個hashmap就可以
Map<String,Class<?>> classMap =new HashMap<String,Class<?>>();
覆寫關鍵方法
不論你是class.forname,還是loadclass方法的呼叫,最後都是要呼叫loadClass的,所以這裡一定要重寫這個方法,這裡就要加入破壞雙親委託機制的邏輯。
public Class<?> loadClass(String name) throws ClassNotFoundException { Class<?> classLoaded = classMap.get(name); if (classLoaded != null) { return classLoaded; } Class<?> findClass = null; try { findClass = findClass(name); } catch (Exception e) { //還可以從父類查詢,這個異常吞掉,如果沒有父類會丟擲 } if (findClass != null) { classMap.put(name, findClass); return findClass; } return super.loadClass(name); }
邏輯很簡單,先從快取找是否已經載入了class,對已經載入的就直接返回,防止重複載入。此處呼叫了findClass方法,findClass是可以覆寫的,這裡為了簡潔的實現,就不再覆寫了。
拿這樣的classloader如果有重複的jar包可能是有問題的。問題就在他的findclass中。findClass找到資源後會通過defineClass(String name, Resource res)來載入類,這裡最後會呼叫getPackage來獲取包。下面是找的流程。
protected Package getPackage(String name) { Package pkg; synchronized (packages) { pkg = packages.get(name); } if (pkg == null) { if (parent != null) { pkg = parent.getPackage(name); } else { pkg = Package.getSystemPackage(name); } if (pkg != null) { synchronized (packages) { Package pkg2 = packages.get(name); if (pkg2 == null) { packages.put(name, pkg); } else { pkg = pkg2; } } } } return pkg; }
這裡很明顯的,先從已經載入的包中查詢,如果沒有就先從父classloader找,最後又進行了雙親委託機制。所以這個地方也需要覆寫。
protected Package getPackage(String name) {
return null;
}
比較暴力直接返回空。在後面的判斷中,如果此處返回null,後續就會重新新建一個物件,然後放入一個快取結構,還是一個hashmap
private final HashMap<String, Package> packages = new HashMap<>();
為空的結果只是更新一下快取。這裡沒有對類載入產生問題。
最終結果
public class MyClassloader extends URLClassLoader {
public MyClassloader(URL[] urls) {
super(urls);
}
Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> classLoaded = classMap.get(name);
if (classLoaded != null) {
return classLoaded;
}
Class<?> findClass = null;
try {
findClass = findClass(name);
} catch (Exception e) {
//還可以從父類查詢,這個異常吞掉,如果沒有父類會丟擲
}
if (findClass != null) {
classMap.put(name, findClass);
return findClass;
}
return super.loadClass(name);
}
@Override
protected Package getPackage(String name) {
return null;
}
}
只要覆寫兩個方法就好。這樣就能比較小的改造成一個破壞雙親委託機制的classloader。
作者:xpbob 連結:https://www.imooc.com/article/details/id/26482 來源:慕課網 本文原創釋出於慕課網 ,轉載請註明出處,謝謝合作