1. Java ClassLoader
Java通過Classloader加載Class,Classloader之間相互隔離。隔離真正的意思是:不同的Classloader可以加載相同的class定義,并且被jvm認定為不同的class。很多人對隔離有誤解,認為不同的Classloader之間不能相互訪問,這其實并不準確。可以這么理解:Classloader只是一個普通的Java類,加載的class集合是它的一個成員變量。如果一個Classloader中維護了另一個Classloader的引用,自然可以通過接口調用查找并使用另一個Classloader加載的類,雙親委派使用的正式這種機制。
雙親委派這名字乍一聽好像是說你有父親、母親兩個Classloader,你可以委派他們兩個先去加載class,他們處理不了的你再處理。如果真這么理解就全錯了。首先,他們不是兩個,可能是n個;其次,他們不是你的父母,他們是整個jvm共享的Classloader;第三,他們誰的父母都不是,因為你和他們沒有血緣(別人也沒有),你不是雙親Classloader的子類。
雙親委派的英文為parent delegation,指的是每個Classloader都有一個getParent方法獲取上一級的Classloader。查找類時,先調用parent Classloader查找,如果找不到在用自身的Classloader查找。層級關系如下:
parent delegation
雙親委派實際上只是引用關系,上述bootrasp、extension、application Classloader之間關系均屬此類。再來看另外一張圖:
自定義Classloader
bootrasp、extension、application Classloader在jvm中只有一個實例,多個用戶自定義的Classloader共享此實例,完成公共部分加載。
2. OSGI Classloader
在OSGI中,每一個Bundle有一個單獨的Classloader實例。更具體點,BundleWiringImpl中定義了一個BundleClassLoader,每當加載一個bundle時,框架創建一個BundleClassLoader實例負責該bundle相關class的加載工作。
BundleClassLoader的加載順序如下:
如class在 java.* package中,委托Bootstrap Classloader處理;
如class定義在 OSGi 框架中啟動委托列表(org.osgi.framework.bootdelegation)中,則將加載請求委托給Bootstrap Classloader處理;
如class在 Import-Package 定義的package中,則框架找到導出此package的 Bundle 的 Class Loader,交其處理 。
如class屬于在 Require-Bundle 中定義的 Bundle,則框架找到導出此package的Bundle的ClassLoader,交其處理。
Bundle 搜索自己的類資源 ( 包括 Bundle-Classpath 里面定義的類路徑和屬于 Bundle 的 Fragment 的類資源);
若類在 DynamicImport-Package 中定義,則開始嘗試在運行環境中尋找符合條件的 Bundle 。
Bundle之間隔離,但如果存在import關系又可以委托給相應export的classloader處理。實現上無非是維護了多個import bundle的Classloader,查找時調用其find方法實現。
需要注意的是:查找時優先查找Import-Package、Require-Bundle中的類,隨后才是查找Bundle自己的類。這里又引入另外一個疑問,Embed-Dependency使用問題。
簡單來說,如果一個Bunlde 需要使用protobuf-java.jar,有如下兩種使用方式:
普通的dependency方式使用,如下圖的Component C的使用方式。
Embed-dependency方式使用,如下圖的ComponentA、ComponentB,此時將protobuf-java做為Bundle自身的一部分使用。
embed-dependency
按類加載的順序,如果先加載Import-Package隨后加載自身的Class,在外部存在protobuf-java,又有內嵌的protobuf-java時,如何做到使用自己的而非外部的呢?答案是在Embed-dependency中的jar并不會出現在Import-Package中。
最后再來講一下,為什么每個bundle需要分配單獨的Classloader,解決什么問題。在我看來,最主要的原因有如下兩個:
定制導出類。非osgi環境下,所有package中的java類都將被導出,無法限定哪些只能jar內使用,哪些是需要export出去的。存在各種誤用,耦合使用情況。
多版本控制。非osgi環境下,一個jvm對于一個類只允許存在一個版本。osgi中每個bundle是獨立開發演進的,可能出現同時存在多個版本。
3. 參考資料