【51CTO譯文】我們曾不只一次的聽到2010年將是Java模塊化的一年的言論;也知道目前為Java提供模塊化的OSGi正在受到IBM和Eclipse基金會的大力支持。但作為實現Java模塊化應用的基礎框架,OSGi似乎並不完美;我們經常能聽到關於OSGi過於復雜的抱怨。
從個人的角度,我以開放的心態去了解OSGi。令人失望的是,我發現它的規則非常復雜而且是低階的(low-level),對於大多數企業 Java 環境,需要對其進行許多改善/縮寫的工作,才能讓它更容易被人理解。對於大多數實際的企業需求,它又顯得功能過於強大。比較而言,Jigsaw 感覺更“干淨”,以 Java 為中心,緊湊而且易於理解。
說實話,這種抱怨讓我有點困惑。我來假設一下,如果OSGi 現在並不存在,有人給我一項任務,為Java 平台設計一個新的模塊系統,那麼對於這個模塊系統的最合理的需求集合將直接指向OSGi,因為OSGi的設計目的可能是滿足我們需求的最簡單的解決方案。
是不是我的想象力不夠?或者這不過是愛因斯坦剃刀原理(事物應盡可能簡單而不是更簡單)打敗奧卡姆剃刀原理(如無必要,勿增實體)的又一個實例?
另外,我認可OSGi 初看並不是那麼簡單這一說法,尤其是你不了解它為什麼會是現在這樣的時候。
OSGi框架的各個組成部分
在這篇文章中,我將按照上述的那個假設,從零開始設計OSGi 系統。當然許多細節問題在這裡我不能一一講述。下面切入正題,為什麼OSGi 成為現在這個樣子?我們一起看看OSGi不算漫長但足夠復雜的進化(這種進化是積極的,因為它為解決業界實際存在的問題而生)。
51CTO編者注:關於OSGi的更多內容可以參考51CTO的專題OSGi入門與實踐全攻略或參考OSGi的入門文章初探Java企業級開源框架OSGi ;關於Java模塊化的內容可以參考51CTO對淘寶網架構師的專訪一步一步了解Java模塊化。
模塊分離
我們的第一個需求是清晰地劃分模塊,這樣一個模塊中的類就不會具有我們無法控制的功能:使用或覆蓋另一個模塊中的類。在傳統的 Java 中有一個“classpath”(類路徑),這是一個巨大的類列表,當多個類碰巧使用相同的名稱時,總是使用第一個類,而第二個和其他所有的同名類將被忽略。看起來這種事情不會經常發生,當事實並非如此。當存在許多庫而這些庫又依靠其他庫時,這個問題就變得常見了。這個覆蓋問題絕對是致命的,因為它會導致一些奇怪的錯誤,比如 LinkageError、IncompatibleClassChangeError 等。事實上能夠看到這些錯誤,那還是比較幸運的。倒霉的是這些錯誤沒有提示,而系統一聲不響地錯誤地運行,哪怕在部署之前我們做了許多先行測試。
對於類的覆蓋和不可能空的可見性,預防方法是為每一個模塊創建一個類加載器(class loader)。類加載器能夠做到僅加載它能夠直接識別的類,在我們的這個系統中,就是某個模塊的內容(不過,它也可以根據類對類的方式,請求其他類加載器提供類,這種方式稱為委派,即 delegation)。使用類加載器之後,每個模塊包括他需要處理的代碼和類,而且能夠保證獲得按照計劃應該使用的類,即使系統中的其他模塊包含同名的類。
從整體上恢復可見性等功能
完成以上步驟之後,我們到達這樣一個點:所有模塊完全隔離,無法互相通信。為了讓這個系統變得實用些,我們需要恢復一些功能,以便能夠看到其他模塊中的類,不過這樣做時必須非常謹慎,而且必須使用嚴格控制的方式。這裡我們又多了一個需求:模塊需要能夠隱藏某些部署細節。
在 Java 中,protected/默認和 public 類型之間缺少訪問修飾符。假設我寫了一個庫,希望這個庫中其他包能夠使用我的一個類,我必須讓這個類設置為 public。但這樣這個類將對所有人是可見的,包括這個庫外部的客戶,這些客戶將能夠直接使用我的內部類。我們想要的是一個“模塊”級的訪問級別,但現在的問題是 javac 編譯器無法區分模塊邊界在哪裡,因此對於這樣的訪問修飾符它無法執行任何檢測。事實上,現有的“默認”訪問修飾符也是有問題的,因為它應該只對同一個“運行時包(runtime package,即由某個特定類加載器加載的包)”提供訪問權。但同樣 javac 無法確定運行時存在哪些加載器。對於這種情況,Javac 會采取冒險的方式:即使之後會導致 IllegalAccessErrors 錯誤,它也會提供訪問權。