三層classloader
Bootstrap classLoader:采用native code實現(xiàn),是JVM的一部分厦取,主要加載JVM自身工作需要的類忆首,如java.lang.*、java.uti.*等谅河; 這些類位于$JAVA_HOME/jre/lib/rt.jar框都。Bootstrap ClassLoader不繼承自ClassLoader,因為它不是一個普通的Java類霎箍,底層由C++編寫奇钞,已嵌入到了JVM內(nèi)核當中,當JVM啟動后漂坏,Bootstrap ClassLoader也隨著啟動景埃,負責加載完核心類庫后,并構(gòu)造Extension ClassLoader和App ClassLoader類加載器顶别。
ExtClassLoader:擴展的class loader谷徙,加載位于$JAVA_HOME/jre/lib/ext目錄下的擴展jar。
AppClassLoader:系統(tǒng)class loader驯绎,父類是ExtClassLoader完慧,加載$CLASSPATH下的目錄和jar;它負責加載應(yīng)用程序主函數(shù)類剩失。
應(yīng)用場景
有時候我們需要在一個 Project 中運行多個不同版本的 jar 包屈尼,以應(yīng)對不同集群的版本或其它的問題。如果這個時候選擇在同一個項目中實現(xiàn)這樣的功能拴孤,那么通常只能選擇更低版本的 jar 包脾歧,因為它們通常是向下兼容的,但是這樣也往往會失去新版本的一些特性或功能乞巧,所以我們需要以擴展的方式引入這些 jar 包涨椒,并通過隔離執(zhí)行,來實現(xiàn)版本的強制對應(yīng)绽媒。
注意事項
總的來說蚕冬,實現(xiàn)隔離允許指定 jar 包,主要需要做到以下幾點:
自定義 ClassLoader是辕,使其 Parent = null囤热,避免其使用系統(tǒng)自帶的 ClassLoader 加載 Class。
在調(diào)用相應(yīng)版本的方法前获三,更改當前線程的 ContextClassLoader旁蔼,避免擴展包的依賴包通過Thread.currentThread().getContextClassLoader()獲取到非自定義的 ClassLoader 進行類加載
通過反射獲取 Method 時锨苏,如果參數(shù)為自定義的類型,一定要使用自定義的 ClassLoader 加載參數(shù)獲取 Class棺聊,然后在獲取 Method伞租,同時參數(shù)也必須轉(zhuǎn)化為使用自定義的 ClassLoade 加載的類型(不同 ClassLoader 加載的同一個類不相等)
OSGI Classloader
在OSGI中,每一個Bundle有一個單獨的Classloader實例限佩。更具體點葵诈,BundleWiringImpl中定義了一個BundleClassLoader,每當加載一個bundle時祟同,框架創(chuàng)建一個BundleClassLoader實例負責該bundle相關(guān)class的加載工作作喘。
BundleClassLoader的加載順序如下:
如class在 java.*? package中,委托Bootstrap Classloader處理晕城;
如class定義在 OSGi 框架中啟動委托列表(org.osgi.framework.bootdelegation)中泞坦,則將加載請求委托給Bootstrap Classloader處理;
如class在 Import-Package 定義的package中砖顷,則框架找到導(dǎo)出此package的 Bundle 的 Class Loader贰锁,交其處理 。
如class屬于在 Require-Bundle 中定義的 Bundle择吊,則框架找到導(dǎo)出此package的Bundle的ClassLoader李根,交其處理槽奕。
Bundle 搜索自己的類資源 ( 包括 Bundle-Classpath 里面定義的類路徑和屬于 Bundle 的 Fragment 的類資源)几睛;
若類在 DynamicImport-Package 中定義,則開始嘗試在運行環(huán)境中尋找符合條件的 Bundle 粤攒。
Bundle之間隔離所森,但如果存在import關(guān)系又可以委托給相應(yīng)export的classloader處理。實現(xiàn)上無非是維護了多個import bundle的Classloader夯接,查找時調(diào)用其find方法實現(xiàn)焕济。
需要注意的是:查找時優(yōu)先查找Import-Package、Require-Bundle中的類盔几,隨后才是查找Bundle自己的類晴弃。這里又引入另外一個疑問,Embed-Dependency使用問題逊拍。
簡單來說上鞠,如果一個Bunlde 需要使用protobuf-java.jar,有如下兩種使用方式:
普通的dependency方式使用芯丧,如下圖的Component C的使用方式芍阎。
Embed-dependency方式使用,如下圖的ComponentA缨恒、ComponentB谴咸,此時將protobuf-java做為Bundle自身的一部分使用轮听。
最后再來講一下,為什么每個bundle需要分配單獨的Classloader岭佳,解決什么問題血巍。在我看來,最主要的原因有如下兩個:
定制導(dǎo)出類珊随。非osgi環(huán)境下藻茂,所有package中的java類都將被導(dǎo)出,無法限定哪些只能jar內(nèi)使用玫恳,哪些是需要export出去的辨赐。存在各種誤用,耦合使用情況京办。
多版本控制掀序。非osgi環(huán)境下,一個jvm對于一個類只允許存在一個版本惭婿。osgi中每個bundle是獨立開發(fā)演進的不恭,可能出現(xiàn)同時存在多個版本。
# OSGi動態(tài)加載刪除bundle
使用監(jiān)聽器?listeners?
ServiceListener?和ServiceTracker?提供bundle和service的動態(tài)監(jiān)聽财饥,ServiceTracker可以動態(tài)監(jiān)聽未來的bundle和service(OSGi Release 2提供的ServiceTracker?换吧,一般推薦)
通過Declarative Service?(OSGi?DS,或者Spring Dynamic Module?(DM))的方式(OSGi Release 4開始钥星,重點推薦U赐摺)