繁体译文 OSGi Module依赖

原文作者:David H Nebinger

原文地址:https://web.liferay.com/web/user.26526/blog/-/blogs/osgi-module-dependencies

如有問題,歡迎在原文下面討論,也歡迎在這裡留言。

 

假設在Liferay DXP平臺上開發module的時候,遇見了需要運行環境(runtime)的依賴的時候,該怎麼辦?

在這片簡短的文章中,我會介紹幾種方法...

假設你有一個module需要iText(和iText的依賴)作為依賴。這其實和module本身的功能關係不是很大,但是你現在有這個依賴,需要一種方法來使用它。

 

方法 1 - 放在全域(global)目錄下

 

這種方法最簡單也最粗暴。所有在全域類載入器(比如tomcat的lib和lib/ext)中的類可以被所有類訪問,包括Liferay OSGi容器。

但是全域的jar有全域的問題。不僅所需要的jar需要在全域目錄,所有jar的依賴也需要在全域目錄。並且全域類只有一個版本,其他的消費類無法使用不同的版本。

 

方法 2 - 讓OSGi處理

 

這個是第二簡單的方法,但是可能無法使用。如果你在module中聲明一個運行環境的依賴,並且OSGi中有一個bundle可以滿足依賴的話,module會使用使用這個依賴。

當你確認OSGi中依賴可以被使用的時候,這個方法就可以使用。因為這個方法是利用portal中已有的依賴,或者你之前已經部署到OSGi容器中的依賴(有些jar可能已經包含了OSGi bundle的資訊,可以直接部署到容器中)。

例如,假設我們要聲明iText依賴,雖然iText應該不會作為bundle已經部署到了OSGi中,所以如果依賴OSGi容器來使用iText很可能會出錯的。現在僅是用作舉例

使用build.gradle檔來聲明運行環境依賴。這段代碼是用來聲明iText運行環境依賴的


runtime group: 'com.iowagie', name: 'itext', version: '1.4.8'

如果iText(和其依賴)已經成功的部署到OSGi容器中,通過運行環境依賴聲明就可以在你自己的module中使用了。如果iText不可用,你的module就不會啟動,並且會報錯 -- 依賴無法滿足。

 

方法 3 - 製作成一個超級胖子Module

 

像那些巨型jar一樣,巨型module會擁有所有依賴的類直接暴露在module jar中。

使用Gradle和BND很好實現。

在build.gradle中,你應該像方法2中一樣聲明運行環境依賴。

並且通過在bnd.bnd中包含所需的資源使module成為一個巨型module:

Include-Resource: @itext-1.4.8.jar

在這裡引入依賴jar,一般在gradle下來依賴或者流覽maven庫的時候都可以看到具體版本。

要注意的是,也需要引入依賴所以依賴的jar。例如,iText2.08依賴於BouncyCasle mail和prov,所以這些依賴也需要添加:

Include-Resource: @itext-2.0.8.jar,@bcmail-138.jar,@bcprov-138.jar

也需要在build.gradle中添加這些依賴,以便gradle引入這些jar。

如果使用zip工具打開module jar包的話,會看見所有依賴的jar會被解壓,class檔會被直接放在module jar包中。

 

方法 4 - 在Module中引入整個jar

 

最後的方法是在module中引入jar,和巨型module不一樣,這個方法將整個jar包含到module jar中

和方法2,3相似的是,也需要在build.gradle中聲明運行環境依賴。

引用jar是在bnd.bnd中完成的。

首先需要定義Bundle-ClassPath屬性來引入module jar和其餘的依賴jar。在下面的例子中,我指定iText jar會包含在module jar中:

Bundle-ClassPath:\
  .,\
  lib/itext.jar

這裡我們不使用Include-Resource聲明,而是使用-includeresource來-將jar引入到bundle中:

-includeresource:\
  lib/itext.jar=itext-1.4.8.jar

在這裡,我們將itext-1.4.8.jar包含到module中,保存為lib/itext.jar。itext-1.4.8是通過Gradle導入的運行環境依賴。

這種語法也支援萬用字元,可以有效利用build.gradle選擇版本的特性。這個例子是用來包含任何版本的commons-lang:

-includeresource:\
  lib/itext.jar=itext-1.4.8.jar,\
  lib/commons-lang.jar=commons-lang=[0-9]*.jar

如果使用壓縮軟體打開module的jar檔的話,可以看到jar檔會在lib資料夾下。

 

總結

 

實際項目中應該使用哪種方法?和Liferay開發一樣,因需而異。

全域方法適用於只需要一種版本的jar包並且需要用到大量的依賴的情況。例如,專案中有20個不同的module全部依賴於iText 1.4.8,這樣,全域方法就是最好的選擇。

第二種方法只用於依賴jar是OSGi bundle的情況。在這種情況下,你可以使用不同的版本,並且不用擔心去編輯bnd檔。

第三種和第四種方法是最常用的放法。在上面這些情境下,你的依賴是包含在module中的,OSGi類容器中的類是不會被不同版本的ja“污染”的。並且,module也是不依賴於當前容器環境的,因為module自己已經包含了所有的依賴,運行環境也不需要為module事前準備依賴。

我個人很喜歡使用第四種方法,以為巨型jar在解壓縮類的時候,可能會有路徑上的交錯(例如xml和設定檔)。第四種方法就不會有這樣的問題。

 

希望你喜歡

 

譯者補充:

在使用方法3,4的時候,OSGi容器會要求導入要引用jar的所有依賴,這樣的結果是,需要導入無盡的包,因為依賴還有依賴,依賴還有依賴,子子孫孫無窮匱也。

 

所以,需要通過bnd.bnd來聲明那些包不需要導入。

 

例如在我使用google-gwt servlet jar的時候。我需要導入這個包:

 

Bundle-ClassPath:\
    .,\
    lib/gwt-servlet.jar,\
    lib/gson.jar

-includeresource:\
    lib/gwt-servlet.jar=gwt-servlet-2.8.0.jar,\
    lib/gson.jar=gson-2.8.0.jar

 

但是卻需要額外聲明,這些包我不需要import

Import-Package: \
    !com.google.gwt.xhr.client.*,\
    !com.google.gwt.core.*,\
    !com.google.gwt.dev.*,\
    !com.google.gwt.i18n.*,\
    !com.google.gwt.json.*,\
    !com.google.gwt.junit.*,\
    !com.google.gwt.thirdparty.*,\
    !com.google.gwt.uibinder.*,\
    !com.google.gwt.user.*,\
    !com.google.gwt.util.*,\
    !jsinterop.*,!javax.annotation.*,!javax.imageio.*,!javax.lang.model.*,\
    !javax.tools.*,!javax.validation.*,!org.apache.*,!org.objectweb.*,\
    !org.w3c.*,!sun.*,!junit.framework,\
    *

其中!是不導入的意思。最後的*是指導入所需要並且在OSGi容器中能導入的。