1. 程式人生 > >如何快速寫一個違背雙親委託機制的classloader

如何快速寫一個違背雙親委託機制的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 來源:慕課網 本文原創釋出於慕課網 ,轉載請註明出處,謝謝合作