Java模塊化解決方案

   網絡上很多OSGi的文章上來就Activator實例,看得雲裏霧裏。要想了解OSGi,首先要知道爲什麼要用

OSGi?它有哪些好處?

   首先要明確:Java缺少對高級模塊化的支持。OSGi服務平臺是專門針對Java對模塊化支持不足的情況,

由OSGi聯盟定義的一個行業標準,它引入了一個面向服務的編程模型,被稱作“VM中的SOA”

Java模塊化的不足

     爲什麼說Java缺少對高級模塊化的支持?Java確實以面向對象的方式提供了某種程度的模塊化,但它從未

考慮支持粗粒度的模塊化編程。主要包括三個方面:

1. 可見性問題 / 信息隱藏

  • There is no mechanism for information hiding between JARs——JAR文件直接無法實現信息隱藏。

一個Java類,可以是public,也可以是package-private;但是一個Java包呢?Java提供了很多控制可見性的

訪問修飾符,但這都是爲了解決低層面向對象封裝,而不是解決邏輯系統劃分。這就導致一個問題:如果類

要在多個包之間可見,那麼就必須是public!

例如,com.alpha.interface包定義了若干接口(public),com.alpha.imp包實現了這些接口
(public);可能你只想向第三方程序暴露com.alpha.interface中的接口,但你卻無法阻止客戶端直接
new一個實現類,因爲你也暴露了 com.alpha.imp包!

如果沒有OSGi,就不得不採用下面這種workaround:

  • 爲了避免暴露非公有API,而把不相干的類放到同一個包中——損害程序的邏輯結構
    而如果保持程序邏輯結構而使用多個包,則又暴露了原本不想暴露的非公有API。

 

2. 易錯的classpath

  • There is no runtime concept that corresponds to a JAR; they are only meaningful at build-time
  • and deploy-time. Once the Java Virtual Ma-chine is running, the contents of all the JARs are simply
  • concatenated and treated as a single, global list: the so-called “Classpath”. This model scales very
  • poorly.
  • They contain no standard metadata to indicate their dependencies.
  • They are not versioned, and multiple versions of JARs cannot be loaded simultaneous.

因爲classpath隱藏了代碼版本、依賴、一致性等特性,所以容易出錯!

例如,同一個項目的不同組件,依賴log4j的不同版本;則類路徑可能會強制選擇某個可能並不合適的版本,

會找到一個並不匹配的Jar,拋出NoSuchMethodError。

——是否可以通過Maven解決?

Class Loading and the Global Classpath

Java的類加載模型如下:


 

ClassLoader有兩個職責:

  • Finding classes, i.e. the physical bytes on disk, given their logical class——如何找類,可擴展
  • Transforming those physical bytes into a Class object in memory.——通過ClassLoader.defineClass
  • ()實現,這個方法是native final的,不可擴展

這種模型確保了類總是會儘可能被最上層的類加載器加載。

例如我們編寫的類會被Application ClassLoader加載,該類加載器按順序查找classpath中的實體,返回第一

個匹配實體。如果classpath中找不到,則報錯ClassNotFoundException.

【總結】JRE的類加載機制是:類加載器只是簡單地在classpath中按順序查找類,並返回第一個匹配類;

JRE看到的不是一個個JAR文件,而是一個class文件列表。

By simply searching classpath entries in order and stopping on the first match, the JRE reduces JARs
to mere lists of class files, which are dumped into a single flat list
這個機制會導致一系列問題:

Conflicting Classes

the classpath will contain conflicting names. 如果classpath中包含衝突的類名,會發生什麼情況?
 
【例1】
java -classpath obsolete.jar:log4j.jar:classes org.example.HelloWorld
本來想運行classes目錄下的HelloWorld,但假如obsolete.jar中也包含一個HelloWorld,因爲它排在classpath的靠前部分,則會執行這個過時的同名的類。
 
【例2】
java -classpath log4j-old.jar;log4j.jar;classes org.example.HelloWorld
本來想用log4j.jar;但log4j-old.jar排在classpath靠前部分,則會加載到這個老jar中的類。

Lack of Explicit Dependencies

大部分Jar都需要依賴其他Jar,但是我們如何知道這種依賴關係?

  1. 靠文檔描述。——不靠譜
  2. 靠MANIFEST.MF/Class-Path描述。——不實用,it only allows one to list further JAR files to be added
  3. to the classpath using absolute file-system paths, or paths relative to the file-system location
Instead the dependency is implicit: lurking inside the class file in the JAR, ready to cause aClassNotFoundException when we try to use it.

Lack of Version Information

we need to specify a version range because depending on a single specific version of a library would make our system brittle.

考慮這麼一個場景:A.jar依賴於Log4j-1.1.jar,B.jar依賴於Log4j-1.2.jar,而Log4j其他版本都會有問題。

我們要把這兩個Log4j版本都加到classpath中,但是JRE總是隻能取到第一個jar;這樣我們要麼修改A.jar,要麼修改B.jar

LinkageError:例如classpath=Log4j-1.2.jar; Log4j-1.1.jar;後一個jar(Log4j-1.1.jar)中
的類都不會被加載到。但是如果後一個jar中有某個類,但是前一個jar中沒有;則這個類還是會被加載
到;這樣程序中會用到來自不同jar的類,可能導致LinkageError。
 

3. 部署和管理支持上的不足

在Java中存在對多個版本的依賴時,沒有簡單的辦法來正確部署這些代碼並執行;

部署之後也不易更新組件;

例如如果要支持動態插件機制,就需要動用類加載器(?)。

總結:JARs Are Not Modules

模塊應該具有如下三個特性:

  • Self-Contained. A module is a logical whole: it can be moved around, installed and uninstalled as a
  • single unit. It is not an atom — it consists of smaller parts — but those parts cannot stand alone, and
  • the module may cease to function if any single part is removed.
Jar文件雖然是物理上的一個獨立單元,可以拷來拷去;但是拷貝之後是否還能正常運行,就不得而知了。因
爲依賴關係可能會缺失,或者變得不正確。
  • Highly Cohesive. Cohesion is a measure of how strongly related or focussed the responsibilities of
  • a module are. A module should not do many unrelated things, but stick to one logical purpose and
  • fulfil that purpose well.
因爲Jar在運行時實際是不存在的,高內聚有何意義?
  • Loosely Coupled. A module should not be concerned with the internal im-plementation of other modules that it interacts with. Loose coupling allows us to change the implementation of one module without needing to update all other modules that use it (along with any modules that use those modules, and so on).
由於信息隱藏的問題,客戶端可以直接訪問jar中的impl而非接口;這樣會導致緊耦合。
 

J2EE解決方案

J2EE應用服務器都具有deployment system,允許動態地部署、解部署應用,而無需重啓服務器、不會影響其他應用。

這意味着標準Java應用所使用的類加載機制是不夠用的;因爲扁平化的全局classpath會導致一個應用中的類,會很容易影響到其他應用。所以J2EE使用了一個更復雜的類加載機制:


 

  • 如果某個類需要在EJB和WAR中共享,則其必須由EAR類加載器加載;
classes that need to be shared across both the EJBs and Web artifacts must be pushed up the tree, into the EAR Class Loader.
  • 如果某個類需要在EAR之間共享,則需要由Application類加載器加載;
And if there are classes we wish to share across multiple deployed applications, we must push them up into the system application class loader. Usually this is done by configuring the way the application server itself is started, adding JARs to the global classpath.

但是這種類就不能動態部署了,並且所有EAR都會依賴這個類,而不管是否需要。

爲了達到動態部署的目的,一般的做法是在每個EAR中分別重複部署該類。

 

OSGi解決方案

每個模塊都有自己獨立的classpath

Java模塊化不足問題的根源是那個全局的、扁平化的classpath;OSGi採取了一個完全不同的方法:每個模塊都有自己獨立的classpath
 

OSGi類加載機制

如何實現這一點呢?OSGi採取了不同的類加載機制:
  • OSGi爲每個bundle提供一個類加載器,該加載器能夠看到bundle Jar文件內部的類和資源;
  • 爲了讓bundle能互相協作,可以基於依賴關係,從一個bundle類加載器委託到另一個bundle類加載器。
 
 
 
Java和J2EE的類加載模型都是層次化的,只能委託給上一層類加載器;
而OSGi類加載模型則是網絡圖狀的,可以在bundle間互相委託。——這樣更合理,因爲bundle間的依賴關係並不是層次化的。
 
例如bundleA、B都依賴於bundleC,當他們訪問bundleC中的類時,就會委託給bundleC的類加載器,由它來查找類;如果它發現還要依賴bundleE中的類,就會再委託給bundleE的類加載器

優點

  • 找不到類時的錯誤提示更友好。假如bundleE不存在,則bundleC就不會被解析成功,會有錯誤消息提示爲何未能解析;而不是報錯ClassNotFoundException或NoClassDefFoundError。
  • 效率更高。在標準Java類加載模型中,總是會在classpath那一長串列表中進行查找;而OSGi類加載器能立即知道去哪裏找類。

解決模塊化問題

  1. OSGi可以幫助你先確保代碼滿足依賴關係,然後才允許執行代碼;避免類路徑錯誤
  2. OSGi會對類路徑上的依賴集進行一致性檢查(如:版本);
  3. 不必擔心由於層次化的類加載模式隱含的限制;——何種限制?
  4. OSGi可以把程序打包邏輯上獨立的JAR文件,並且只部署指定的部分、動態部署;——OSGi生命週期層實現動態部署
  5. OSGi可以聲明JAR中的哪些代碼可以被其他JAR訪問,強化可見性 ——OSGi中,只有那些被顯式導出的包才能被其他bundle使用。也就是說默認情況下,所有包都是bundle private的,不能被其他包看到。
  6. OSGi爲程序定義了一個插件式的擴展機制。
In OSGi, only packages that are explicitly exported from a bundle can be imported and used in another bundle. Therefore all packages are “bundle private” by default, making it easy for us to hide the internal implementation details of our bundles from clients.
 
 
轉自:http://blog.csdn.net/vking_wang/article/details/12379333
發佈了36 篇原創文章 · 獲贊 3 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章