当前位置:网站首页>[轉]: OSGI規範 深入淺出

[轉]: OSGI規範 深入淺出

2022-07-05 05:10:00 morpheusWB

OSGI規範 深入淺出

一、OSGI簡介

1、OSGI簡介

OSGI(Open Service Gateway Initiative),即開放服務網關協議,是面向Java的動態模型系統。
OSGI是指由OSGI Alliance組織制定的Java模塊化規範,OSGI規範的核心部分是一個框架,其中定義了應用程序的生命周期模式和服務注册。基於OSGI框架定義了大量的OSGI服務:日志、配置管理,HTTP服務(運行Servlet)、XML解析、設備訪問、軟件包管理、許可管理、用戶管理、IO連接、Jini和UPnP等。
OSGI中文社區:http://www.osgi.com.cn/ 
OSGI官方網站:https://www.osgi.org/
OSGI框架實現了一個優雅、完整和動態的組件模型,組件(bundle)無需重新引導可以被遠程安裝、啟動、昇級和卸載。
OSGI服務平臺提供在多種網絡設備上無需重啟的動態改變構造的功能。
為了最小化耦合度和促使耦合度可管理,OSGI技術提供了一種面向服務的架構,使組件動態地發現對方。
OSGI聯盟已經開發了如HTTP服務器、配置、日志、安全、用戶管理、XML等很多公共功能標准組件接口。標准組件的兼容性插件實現可以從不同計算機服務提供商得到。
OSGi的主要職責就是為了讓開發者能够創建動態化、模塊化的Java系統。

2、OSGI規範的組成

OSGI規範包括以下子規範:
A、Framework規範(OSGI核心,提供一個安全的可管理的Java Framework來部署可擴展的Java服務)
B、Package Admin Service規範(管理不同的Bundle之間的引用關系。當Bundle更新或者卸載時判斷是否有其它的服務正在使用當前的Bundle)
C、Start Level規範(定義了啟動和停止一個OSGi Service Platform時,不同的Bundles的啟動或者停止的先後順序)
D、Permission Admin Service規範(Bundle是否許可執行另外的Bundle的代碼)
E、URL Handlers Service規範(怎樣注册URL Schema,如何將java.io.InputStream對象轉換為特定的Java對象)
F、Log Service規範
G、Configuration Admin Service規範
H、Device Access Specification
I、User Admin Service Specification
J、IO Connector Service Specification
K、Http Service Specification
L、Preference Service Specification
M、Wire Admin Service Specification
N、XML Parser Service Specification
O、Metatype Specification
P、Service Tracker Specification
Q、Measurment and State Specification
R、Position Specification
S、Execution Environment Specfication

3、OSGI的優點

OSGI的優勢主要錶現在以下幾個方面:

A、熱插拔的插件體系結構

基於OSGI的應用程序可動態更改運行狀態和行為。在OSGI框架中,每一個組件都是可熱插拔的,因此,對某一特定的組件的修改並不會影響到容器中的所有組件,運行中的大部分組件依舊能照常工作,部署一個新的Bundle時也不需要重新啟動服務器。

B、可複用性

OSGI框架本身可複用性極强,易於構建真正面向接口的程序架構,每一個組件(Bundle)都是一個獨立可複用的單元。基於OSGI獨特的特性,進行企業開發的時候,對於新的開發,可以從企業的組件庫中精簡出可複用的模塊,量身定做新的組件,最大限度的利用了已有的資源,降低開發成本,增强企業的競爭力。

C、高效性,穩定性

OSGI是一個穩定而高效的系統。OSGI作為一個微核的系統,其核心只有為數不多的幾個JAR包。基於OSGI框架的系統的低耦合性,其結構的優勢性保證具體的某一個組件不至於影響到全局,更不會因為局部的錯誤導致全局系統的崩潰。

4、OSGI的缺點

A、每個組件(Bundle)都由單獨的類加載器加載,與一些Java EE項目中使用比較多的框架整合比較困難,如Spring MVC、Struts2等。
B、目前OSGI框架提供的管理端不够强大,現在的管理端中僅提供了基本的組件狀態管理、日志查看等功能,像動態修改系統級別的配置(config.ini)、動態修改組件的配置(Manifest.mf)、啟動級別等功能都尚未提供。
C、采用OSGI作為規範的模塊開發、部署方式自然給現有開發人員提出了新的要求,需要學習新的基於OSGI的開發方式。

二、OSGI框架原理

1、OSGI框架簡介

OSGI框架從概念上可以分為三層:模塊層、生命周期層和服務層。
Module Layer:模塊層主要涉及包及共享的代碼;
Lifecycle Layer:生命周期層主要涉及組件的運行時生命周期管理;
Service Layer:服務層主要涉及模塊之間的交互和通信。

架構設計——OSGI規範

OSGI Framework是OSGI Service Platform規範的核心組成部分,提供了一個通用的、安全可管理的Java framework。通過OSGI Framework可以支持一種叫做組件的Service application的部署和擴展。
OSGI兼容設備可以下載並且安裝OSGI組件,也可一旦不再需要的時候删除。組件安裝後會注册一定數量的Services,並被由同一個Framework下的其它組件使用。
在一個動態擴展的的OSGI環境中,Framework管理組件的安裝和更新,同時也管理組件和Services之間的依賴關系。
Framework提供給組件開發者必須的資源來在Java平臺上開發,為開發的組件提供了代碼動態加載的功能, 也使得開發者開發、部署一個大規模的Services變的很容易。
其次,Framework為Java組件開發者提供了簡明一致的編程模型,簡化了開發部署的複雜性。編程模型允許開發者將自己的接口規範綁定到OSGI環境中的Service。
一個一致的編程模型幫助開發者可以應付一些可估計的危急錯誤。Framework將會運行在不同的硬件環境上,但一致的接口確保組件可以運行在一致的服務接口上。

2、模塊層

模塊層是OSGi框架中最基礎的部分。
OSGi的模塊化,是通過為Jar包添加metadata 來定義哪些類該暴露,哪些類該隱藏,其控制單元叫做組件Bundle(jar包)。 
Bundle是以jar包形式存在的一個模塊化物理單元,包含代碼、資源文件和元數據(metadata),並且jar包的物理邊界也是運行時邏輯模塊的封裝邊界。
Bundle是OSGi中的基本組件,其錶現形式仍然為Java概念中傳統的Jar包。
通過META-INF目錄下的MANIFEST.MF文件對其予以進一步的定義。
通常一個MANIFEST.MF文件的內容如下:

Manifest-Version: 1.0Bundle-ManifestVersion: 2Bundle-Name: UtilBundle-SymbolicName: com.ibm.director.la.utilBundle-Version: 1.0.0Bundle-RequiredExecutionEnvironment: J2SE-1.5Import-Package: org.osgi.framework;version="1.3.0"Export-Package: com.ibm.director.la.util;uses:="org.osgi.framework"Bundle-ClassPath: lib/junit.jar,

MANIFEST.MF文件存儲的實際上是Bundle的元數據。
元數據的內容可以精確的定義Bundle的各種特征,同時能更好的對Bundle進行標識同時幫助用戶對Bundle進行理解。

3、生命周期層

生命周期層在OSGi框架中屬於模塊層上面的一層,生命周期層的運作是建立在模塊層的功能之上的。
生命周期層的主要功能是控制動態安裝、開啟、關閉、更新和卸載組件。
生命周期層能够從外部管理應用或者建立能够自我管理的應用(或者兩者的結合),並且給了應用本身很大的動態性。 
Bundle的使用需要生命周期層的API與OSGi框架的生命周期層進行交互。

4、服務層

OSGi服務是注册到OSGi框架中的一個Java對象。注册的時候可以設置Service的屬性。而在獲取Service的時候可以根據屬性進行過濾。
Bundle可以通過Bundle的上下文去注册Service或去查詢Service。

三、模塊層

1、模塊化簡介

模塊化是將一個大型系統分解為多個較小的互相協作的邏輯單元,通過强制設定模塊之間的邏輯邊界來改善系統的維護性和封裝性。

架構設計——OSGI規範

模塊(module)定義了一個邏輯邊界,模塊本身精確地控制哪些類是完全被封裝起來的,而哪些類需要暴露出來作為外部使用。開開發者可以輕松地將實現屏蔽在模塊的內部,將公共API暴露在外部。

2、OSGI模塊化與面向對象

面向對象編程中不會把所有功能都塞到同一個類。面向對象編程從問題域中發現多個事物,每個事物負責不同的功能,盡量做到高內聚和低耦合。面向對象的模塊化粒度在類級別上。 
OSGi的模塊化是通過為JAR包添加metadata來定義哪些類應該暴露哪些類又隱藏在包中,其控制可見性的粒度是在組件(JAR包)層面上的。 
OSGI模塊化和面向對象思想所帶來的能力都是通過控制可見性和可用性來保證高內聚和低耦合的,但粒度不同,一個是組件層面上,一個是對象層面上。

3、JAVA在模塊化方面的局限

A、底層代碼的可見性控制

Java提供了private、public、protected和package private(無修飾符)四種訪問控制級別,提供了底層的OO數據封裝特性。Packege具有分割代碼的作用,但如果包中的代碼要對包外可見,必須設置為public(protected,使用繼承)。 
org.serc.helloworld.Hello.java:定義了一個接口

package org.serc.helloworld;  public interface Hello {      void sayHello();  }


org.serc.helloworld.impl.HelloImpl.java:實現了Hello接口

package org.serc.helloworld.impl;import org.serc.helloworld.Hello;public class HelloImpl implements Hello{    final String helloString;    public HelloImpl(String helloString){        this.helloString = helloString;    }    public void sayHello(){        System.out.println(this.helloString);    }}


org.serc.helloworld.main.Main.java文件:

package org.serc.helloworld.main;import org.serc.helloworld.Hello;import org.serc.helloworld.HelloImpl;public class Main{    final String helloString;    public static void main(String[] args){        Hello hello = new HelloImpl(“Hello,SERC!”);        hello.sayHello();    }}


三個文件分別在不同的包中。HelloImpl實現細節不應該暴露給其它包,但從Main.java的main方法中可以看出,為了創建Hello的實例,必須引入HelloImpl類,但是HelloImpl作為接口的實現細節,不應該暴露給使用者,違反了封裝的原則。 
如果不想讓HelloImpl暴露出來,需要做額外的工作保證既隱藏了實現細節,又能簡單的創建一個實現了Hello接口的實例。可以通過多種方法(比如工廠模式)來實現,但增加了與應用本身功能無關的多餘工作,是Java的局限。

B、classpath的局限

在classpath中加入jar包的時候,只給出文件路徑,而jar包的版本、一致性、依賴性,無法在classpath中明確的設置或是從classpath中看出相應屬性。 
classpath中的jar包是按序加載的,例如: 
classpath=/servlet2.2/servlet.jar;/servlet2.3/servlet.jar, 在實際應用的過程中,Java使用servlet2.2,而不是servlet2.3。如果在大型系統中團隊分開開發時,各用各的servlet包,並且版本號不一樣,在最後將開發結果合並的時候,用的是哪個版本的servlet包就很難搞清楚。 
即使classpath能注意到版本的問題,也沒法精確指出依賴。開發者需要根據提示信息增加依賴包,直到虛擬機不運行到缺包异常為止。 

C、OSGI對JAVA局限的改善

OSGi中很好解决了JAVA的局限性:
包的可見性:OSGi通過引入包的可見性機制,能够完全控制一個包中的代碼對哪些模塊可見,而不僅僅局限於無差別的可見性,從而完善了Java的代碼訪問控制機制。
包的版本:OSGi通過為包增加版本信息,可以精確控制代碼的依賴,保證代碼的版本一致性,彌補了classpath的缺點。

4、組件Bundle簡介

組件(bundle)是以jar包形式存在的一個模塊化物理單元,包含了代碼、資源文件和元數據(metadata),並且jar包的物理邊界也是運行時邏輯模塊的封裝邊界。 

架構設計——OSGI規範

在標准的jar包的manifest文件中添加一些組件(bundle)的模塊化特征(metadata)後,jar包就變成了一個bundle。 
bundle和普通jar包最大的區別就在於元數據。
Bundle元數據的目的在於准確描述模塊化相關的bundle特征,讓OSGi框架對bundle進行各種處理工作(比如依賴解析,强制封裝等),元數據主要有三部分: 

A、可讀信息(可選)

OSGi標准定義了幾個元數據條目幫助更好地理解和使用bundle,但所有的條目都不是必須的,並且不對模塊化特性產生任何的影響,OSGi框架會完全無視可讀信息。

Bundle-Name: SERC HelloworldBundle-Vendor: GR, SERCBundle-DocURL: http://elevenframework.orgBundle-Category: exampleBundle-Copyright: SERC  

B、bundle標識符(必須)

bundle標識符用於唯一的標識一個bundle。
早期的OSGi標准中並沒有提供標識一個已知bundle的方法,直到OSGi R4標准,“唯一bundle標識符”被提出來。為了向後兼容,Bundle-Name不能用來作為標識符,否則就會增加維護向後兼容的工作,所以使用新的manifest屬性Bundle-SymbolicName。

Bundle-SymbolicName: org.serc.helloworld

Bundle-Name是給用戶讀的,而Bundle-SymbolicName是給OSGi框架讀的,讓OSGi框架能够唯一標識一個bundle。 
只用一個Bundle-SymbolicName肯定是可以唯一標識一個bundle,但是隨著時間的推移,bundle可能會有新版本,加入版本屬性會讓bundle的信息更加准確。

Bundle-Name: SERC HelloworldBundle-Vendor: GR, SERCBundle-DocURL: http://elevenframework.orgBundle-Category: exampleBundle-Copyright: SERC  

C、代碼可見性(必須)

代碼可見性用於定義內部與外部代碼。在JavaSE中的jar包如果放在classpath裏,那麼jar包對classpath下的所有程序都是可見的,並且可見性不能改變。而OSGi標准定義了如下的屬性用於描述代碼的可見性:
Bundle-ClassPath:定義了形成bundle的所有代碼所在的比特置,Java 中的classpath是定義的jar包的比特置,而Bundle-ClassPath屬性描述的是bundle內部類在bundle中的路徑。例如:

Bundle-ClassPath:.,other-classes/,embedded.jar

Export-Package:顯式暴露需要和其它bundle共享的代碼,每個包之間用逗號分隔,每個包可用修飾詞來修飾包的其它特征。

Export-Package: org.serc.hellworld; vendor=”SERC”,org.serc.hellworld.impl; vendor=”Gou Rui” 

Import-Package:定義bundle所依賴的外部代碼,其格式和Export-Package相同,並且也可以使用修飾詞來修飾包。修飾詞是用來限制所依賴包的範圍的,類似過濾器,而不像Export-Package中用來聲明包的特征。例如: 

Import-Package: org.serc.helloworld; vendor=”SERC”

四、生命周期層

1、生命周期層簡介

生命周期層在OSGi框架中屬於模塊層上面的一層,其運作建立在模塊層的功能之上的。生命周期層主要功能是讓開發者能够從外部管理應用或者建立能够自我管理的應用(或者兩者的結合),並且給應用本身很大的動態性。

2、生命周期管理簡介

一般來說,程序(或者程序的一部分)都一定服從某種生命周期。軟件的生命周期有4個典型的階段,如下:

架構設計——OSGI規範

如果正在創建一個應用,首先得安裝(install)應用;當應用的所有依賴都滿足,可以執行應用;如果應用不再需要,可以停止(stop);一段時間後,可能需要更新(update)應用的版本;最終,可能會移除(remove)應用。 
通過在外部或者內部對應用進行操作,完成對應用的生命周期管理過程。對於非模塊化應用,操作是以整個應用為對象的;對於模塊化應用,可以有更細粒度(針對應用中的某個模塊)的生命周期管理。

3、OSGi bundle生命周期

要想使用組件(bundle),就得使用生命周期層的API與OSGi框架的生命周期層進行交互。
OSGi框架的核心並沒有强制使用任何特定的API交互機制(比如命令行,GUI,或者XML配置文件等),只是單純的Java API而已,開發者可以任意創造出自己想要的交互模式,保證了框架的靈活性。 
在標准的Java編程中,會通過將jar包放到classpath中來使用jar包,而bundle不同。Bundle只有在被安裝(install)到一個OSGi框架的運行實例中才能用起來,並且OSGi框架支持對bundle完整的生命周期管理,並且支持這些管理操作在應用執行完成,其動態性可見一斑。 
Bundle生命周期的狀態轉移圖如下:

架構設計——OSGI規範

可以通過Bundle的getState方法來獲得bundle的當前狀態。
Starting和Stopping狀態是暫態,在持續一會兒後就會自動轉移到下一個狀態,不需要轉移條件。

4、生命周期層API

生命周期層的API主要由BundleActivator、BundleContext、Bundle三個核心接口組成。

A、BundleActivator

BundleActivator讓開發者能够捕捉bundle的start和stop事件,並對作出自定義的反應。 
BundleActivator的接口定義如下:

public interface BundleActivator {    public void start(BundleContext context) throws Exception;    public void stop(Bundlecontext context) throws Exception;}

如果一個類實現了BundleActivator接口,那麼類就成為一個Activator。但有實現是不够的,要讓OSGi框架知道Activator的存在。所以還需要在MANIFEST文件中添加如下一項屬性: 

Bundle-Activator:org.demo.Activator

當bundle啟動(start)的時候,OSGi框架就會調用Activator的start方法,同樣的也適用與stop方法。 
並不是每個bundle都需要一個activator,有時候bundle只是為了和其它bundle分享代碼,而並不需要在啟動和停止的時候做出多餘的動作。是否使用BundleActivator借口,需要具體問題具體分析。 

B、BundleContext

BundleContext是bundle在框架中的執行時上下文,提供了與框架進行交互的方法。 
BundleContext接口中的方法主要分為兩類,一類與部署和生命周期管理相關,另一類是關於利用服務層進行bundle間交互的方法。

public interface BundleContext {    String getProperty(String key);    Bundle getBundle();    Bundle installBundle(String location, InputStream input) throws BundleException;    Bundle installBundle(String location) throws BundleException;    Bundle getBundle(long id);    Bundle[] getBundles();    void addBundleListener(BundleListener listener);    void removeBundleListener(BundleListener listener);    void addFrameworkListener(FrameworkListener listener);    void removeFrameworkListener(FrameworkListener listener);}

BundleContext接口對於與其相關的bundle來說都是唯一的執行上下文,並且只有在bundle是屬於active狀態的時候執行時上下文才是有意義的,即在start方法被調用和stop方法被調用的兩個時間點之間。如果一個bundle沒有處於active時間段,但組件的bundlecontext對象被使用,框架會拋出异常。 
框架使用這個上下文對象還有一個目的就是為了bundle的安全和資源分配,所以BundleContext對象應該被當做私有對象,不應該被隨意在bundle之間傳遞。

C、Bundle

Bundle在邏輯上錶示一個bundle,OSGi環境中的一個物理bundle對應一個bundle對象。bundle對象中包含了bundle的基本信息和bundle聲明周期的控制接口。 
在BundleContext接口中,getBundle方法可以得到Bundle對象。 
對於每個被安裝到框架中的bundle,框架都創建一個Bundle對象在邏輯上錶達之。Bundle接口中定義了bundle生命周期管理的方法:

public interface Bundle {    BundleContext getBundleContext();    long getBundleId();    Dictionary getHeaders();    Dictionary getHeaders(String locale);    String getLocation();    int getState();    String getSymbolicName();    Version getVersion();    void start(int options) throws BundleException;    void start() throws BundleException;    void stop(int options) throws BundleException;    void stop() throws BundleException;    void update(InputStream input) throws BundleException;    void update() throws BundleException;    void uninstall() throws BundleException;}

大部分OSGi框架的實現都是將locatioin解釋為指向OSGi bundle的一個URL,在需要的時候就會通過URL將bundle下載到框架中來安裝使用。但OSGi標准沒有規定location的形式必須是URL,而且URL也並不是非要不可的,還可以通過輸入流(Input Stream)來安裝bundle。 
bundle不能自己改變自己的狀態,比如說一個active的bundle不能stop自己,stop自己就會拋出异常。

五、服務層

1、服務簡介

服務是服務提供者和服務使用者之間的一個契約。使用者一般不關心服務實現的細節,只關心是否滿足契約(服務應該提供什麼功能,滿足什麼格式)。使用服務的過程包含了發現服務和達成協議的形式,需要通過服務的標志性特征來找到對應的服務。

2、OSGI服務層

服務層是OSGi框架中最上面的一層,服務層帶來了更多的動態性,並且使用了面向服務編程模型。當一個bundle發現開始使用OSGi中的一個服務後,服務可能在任何的時候改變或者是消失。 
OSGi框架有一個中心化的注册錶,注册錶遵從publish-find-bind模型: 

架構設計——OSGI規範

一個提供服務的bundle可以發布POJO作為服務的實體;一個使用服務的bundle可以通過注册錶找到和綁定服務。 
可以通過BundleContext接口來完成服務的發布、發現、綁定,如下:

public interface BundleContext {       void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException;      void addServiceListener(ServiceListener listener);      void removeServiceListener(ServiceListener listener);      ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties);       ServiceRegistration registerService(String clazz, Object service, Dictionary properties);      ServiceRegistration[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException;      ServiceRegistration[] getAllServiceReferences(String clazz, String filter) throws InvalidSyntaxException;      ServiceReference getServiceReference(String clazz);      Object getService(ServiceReference reference);      boolean ungetService(ServiceReference reference);  }

3、發布服務

為了讓其它bundle能發現服務,必須在發布服務前對其進行特征描述。服務特征包括接口的名字(可以是名字的數組),接口的實現和一個可選的java.util.Dictionary類型的元數據信息。示例如下:

String[] interfaces =  new String[]{StockListing.class.getName(), StockChart.class.getname()};  Dictionary metadata =  new Properties();  metadata.setProperty("name", "LSE");  metadata.setProperty("currency", Currency.getInstance("GBP"));  metadata.setProperty("country", "GB");  ServiceRegistration registration = bundleContext.registerService(interfaces, new LSE(), metadata);

上述代碼中,通過ServiceRegistration對象可以更新服務的元數據: 

registration.setProperties(newMetadata);

可以直接將服務注銷: 

registration.unregister();

ServiceRegistration對象不能和其它Bundles共享,因為ServiceRegistration對象和發布服務的bundle的生命周期相互依存。如果bundle已經不在框架執行環境中存在,那麼ServiceRegistration對象也不應該存在。 
代碼中的參數new LSE()是一個POJO,不需要實現任何OSGi類型或者使用標注,只要滿足服務約定就可以。 
如果在删除發布的服務前bundle以及停止,框架會幫助删除服務。

4、發現服務

可以根據服務約定從注册錶中找到正確的服務。發現服務並獲得其引用的接口如下:

ServiceReference reference = bundleContext.getServiceReference(StockListing.class.getName());  

reference是服務對象的間接引用,不直接使用服務對象是為了將服務的使用和服務的實現進行解耦,將服務注册錶作為兩者的中間人,達到跟踪和控制服務的目的,同時還可以在服務消失後通知使用者。 
ServiceReference可以在bundle之間互享,與使用服務的bundle的生命周期無關。 
在getServiceReference方法中,選擇service的默認優先級是先選擇service.rank最高的,在rank相等的情况下選擇最早在框架中注册的。除了默認的規則,還可以在getServiceReferences中通過添加過濾參數(作為調用該方法的第二個參數)來做一些篩選。

ServiceReference[] references = bundleContext.getServiceReferences(StockListing.class.getName(),                                 "(&(currency=GBP)(objectClass=org.example.StockChart))");

匹配參數是一個字符串,字符串的格式屬於LDAP查詢格式,在RFC1960標准中有完整的描述。 
字符串中等號左邊的內容是元數據(Dictionary)中的左值,通過左值對應的右值與服務的元數據進行匹配。匹配示例如下: 
屬性匹配:

(name=John Smith) (age>=20) (age<=65) 

模糊匹配: 

(name~=johnsmith)

通配符匹配: 

(name=Jo*n*Smith*)

判斷某個屬性是否存在: 

(name=)

條件與: 

(&(name=John Smith)(occupation=doctor))

條件或: 

(|(name~=John Smith)(name~=Smith John))

*條件非: ** 

(!(name=John Smith))

5、綁定和使用服務

發現服務後,使用服務之前,必須從注册錶中綁定實現的服務。

StockListing listing = (StockListing) bundleContext.getService(reference);

返回的POJO實例和在注册錶中注册的實例是同一個。 
每次使用getService方法的時候,注册錶會將對應服務的使用次數加1,同時會記錄誰在使用該服務。如果不想使用服務的,注銷服務。

bundleContext.ungetService(reference); listing = null; 

六、OSGI的實現

1、OSGI的具體實現

OSGI是OSGi Alliance組織制定的Java模塊化規範,但OSGI聯盟並沒有給出OSGI容器的實現,具體實現由第三方廠商完成,目前使用較多的OSGI容器有Apache Felix、Equinox、Spring DM。

2、OSGI的JAVA實現

A、Apache Felix

Apache Felix是Apache旗下的一個OSGi框架,Felix是一個OSGi版本4規範的Apache實現。Apache Felix提供的服務幾乎涵蓋了全部的OSGi 4.2的標准,除此之外還提供了一些非標准的功能,例如iPOJO。Apache Felix框架本身非常緊凑,只需要3個包加一個shell就可以運行,無論是開發還是Debug都非常簡便。

B、Equinox

Equinox是Eclipse旗下的OSGi框架,本身也被Eclipse采用,是Eclipse的PDE開發環境的底層。Equinox本身也是相當的全面的框架,提供的功能不比Felix少多少。Equinox被當做開發Eclipse Plugin的應用較多,如果要開發一個Web程序,就會感到功能和文檔不够全面。Equinox最大的優勢在於和Eclipse結合緊密,只要安裝了PDE,就已經有了Equinox,可以方便的在Eclipse裏設置開發的Bundle,啟動、部署等操作也异常簡單,而且有專門的Debug界面。

C、Spring DM

Spring DM是Spring旗下的OSGi框架,Spring DM的最大特點是結合了Spring框架。

D、Knopflerfish

Knopflerfish是OSGi的先行者,是一個相當標准OSGi框架,提供了絕大多數標准功能。

3、OSGI的C++實現

A、CTK Plugin Framework

CTK是基於Qt開發的支持生物醫學影像計算的開源項目。
CTK中的CTK Plugin Framework模塊借鑒了OSGI的思想,並實現了幾乎完整的OSGI框架API。

B、C++ Micro Services

C++ Micro Services是基於OSGI思想的用於創建和管理模塊化軟件系統的C++庫。

4、OSGI的C實現

Apache Celix是基於C/C++的OSGI規範實現,提供了一個使用組件和面向服務編程(SOP)開發模塊化應用的框架。
Apache Celix主要使用C語言開發,為了支持C++,以庫的形式增加了抽象。
官方網站:http://celix.apache.org/

七、OSGI應用實例

我們需要解决一下幾問題:

1.如何正確的理解和認識OSGI技術?

我們來回到我們以前的某些開發場景中去,假設我們使用SSH(struts+spring+hibernate)框架來開發我們的Web項目,我們做產品設計和開發的時候都是分模塊的,我們分模塊的目的就是實現模塊之間的“解耦”,更進一步的目的是方便對一個項目的控制和管理。
我們對一個項目進行模塊化分解之後,我們就可以把不同模塊交給不同的開發人員來完成開發,然後項目經理把大家完成的模塊集中在一起,然後拼裝成一個最終的產品。一般我們開發都是這樣的基本情况。

那麼我們開發的時候預計的是系統的功能,根據系統的功能來進行模塊的劃分,也就是說,這個產品的功能或客戶的需求是劃分的重要依據。

但是我們在開發過程中,我們模塊之間還要彼此保持聯系,比如A模塊要從B模塊拿到一些數據,而B模塊可能要調用C模塊中的一些方法(除了公共底層的工具類之外)。所以這些模塊只是一種邏輯意義上的劃分。

最重要的一點是,我們把最終的項目要去部署到tomcat或者jBoss的服務器中去部署。那麼我們啟動服務器的時候,能不能關閉項目的某個模塊或功能呢?很明顯是做不到的,一旦服務器啟動,所有模塊就要一起啟動,都要占用服務器資源,所以關閉不了模塊,假設能强制拿掉,就會影響其它的功能。

以上就是我們傳統模塊式開發的一些局限性。

我們做軟件開發一直在追求一個境界,就是模塊之間的真正“解耦”、“分離”,這樣我們在軟件的管理和開發上面就會更加的靈活,甚至包括給客戶部署項目的時候都可以做到更加的靈活可控。但是我們以前使用SSH框架等架構模式進行產品開發的時候我們是達不到這種要求的。

所以我們“架構師”或頂尖的技術高手都在為模塊化開發努力的摸索和嘗試,然後我們的OSGI的技術規範就應運而生。

現在我們的OSGI技術就可以滿足我們之前所說的境界:在不同的模塊中做到徹底的分離,而不是邏輯意義上的分離,是物理上的分離,也就是說在運行部署之後都可以在不停止服務器的時候直接把某些模塊拿下來,其他模塊的功能也不受影響。

由此,OSGI技術將來會變得非常的重要,因為它在實現模塊化解耦的路上,走得比現在大家經常所用的SSH框架走的更遠。這個技術在未來大規模、高訪問、高並發的Java模塊化開發領域,或者是項目規範化管理中,會大大超過SSH等框架的地比特。

現在主流的一些應用服務器,Oracle的weblogic服務器,IBM的WebSphere,JBoss,還有Sun公司的glassfish服務器,都對OSGI提供了强大的支持,都是在OSGI的技術基礎上實現的。有那麼多的大型廠商支持OSGI這門技術,我們既可以看到OSGI技術的重要性。所以將來OSGI是將來非常重要的技術。

但是OSGI仍然脫離不了框架的支持,因為OSGI本身也使用了很多spring等框架的基本控件(因為要實現AOP依賴注入等功能),但是哪個項目又不去依賴第三方jar呢?

2.OSGI技術對我們項目的開發有什麼幫助?

(1)項目展示

接下來我們同過項目代碼來展示一下OSGI的魅力:
我們先不要去急著理解如何使用OSGI,我們通過一個項目先來看一下OSGI的效果。
(以下工程代碼是網上教學視頻中的樣例,源碼我這裏是沒有的)
(提前說一下:我們要學習的重點就是我們這個購物網站如何結合OSGI技術,使得項目更加的靈活可控,而購物網站本身並不是重點。)
首先在Eclipse中先打開我們的單服務器版本的項目:

https://img-blog.csdn.net/20160310173347989?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

啟動成功:

https://img-blog.csdn.net/20160310173401567?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

這是一個Web項目,我們打開瀏覽器看一下效果:

https://img-blog.csdn.net/20160310173429161?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

可以看出是一個網上購物的項目。

我們來看一下我們基於OSGI技術的項目和我們一般的項目有什麼區別。
首先介紹一下這個項目的模塊:

1.大類展示

https://img-blog.csdn.net/20160310173537333?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
2.小類展示(大類的子產品)

https://img-blog.csdn.net/20160310173552836?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

點進去之後就是產品的具體信息

https://img-blog.csdn.net/20160310173601523?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

3.購物車
沒買東西是空的:

https://img-blog.csdn.net/20160310173611224?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

買完之後:

https://img-blog.csdn.net/20160310173619274?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

4.商品管理(上架、下架)

可以看到,這個項目和我們平常開發的項目沒有什麼不同(我知道界面很簡陋= =),重點是它的啟動和加載過程。

https://img-blog.csdn.net/20160310173629587?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

(2)關於服務器

我們是通過動態加載,也就是“熱部署”來啟動我們的項目的。就是說,我們這個項目把它放在Web容器中之後,我們可以將某些功能給它拿下來,而且拿下來的時候不會對其他模塊造成影響。

我們以前運行tomcat的時候,啟動一下服務器,將Web項目一次性裝載完畢,控制臺會出現類似這種信息:

https://img-blog.csdn.net/20160310173643287?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
但是我們啟動這個項目的時候並不是這樣:

https://img-blog.csdn.net/20160310173703556?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
那麼我們沒有用tomcat和jBoss,那是如何部署和啟動Web項目的呢?不可能沒有Web服務器中間件的啊?這裏告訴大家,OSGI技術裏面也是內嵌了一個Web服務器的,就是jetty。

 

我們打開這個項目的Run Configuration配置窗口,看一下運行這個項目所需要的插件包:

https://img-blog.csdn.net/20160310173803120?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

可以看到,除了一些Web項目需要的jar包,還是有jetty的存在的。所以用到的服務器是jetty,不再是tomcat。


大家可能還是比較熟悉tomcat,對於jetty不是太熟悉,那麼我們簡單介紹一下jetty:
jetty也是一個比較優秀的Web容器,在某些性能方面要比tomcat强大的多(如高並發,長連接)。而且它的整個結構比tomcat輕巧很多(tomcat更臃腫),具體區別大家可以去網上自己看一下。


(3)運行模式和插件

我們接下來正式看一下此項目在OSGI下的運行模式:
我們在啟動的時候,加載了四個模塊,分別是:

https://img-blog.csdn.net/20160310173838100?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

按照模塊化的思想他們就是四個對應的功能模塊。
他們對應的四個功能模塊的工程代碼我們可以在Eclipse中看到:

我們看一下我們的啟動配置(依然打開是Run Configuration配置窗口):

https://img-blog.csdn.net/20160310173851028?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

https://img-blog.csdn.net/20160310173908287?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

配置分為“WorkSpace”和“Target Platform”,分別是我們工作空間(我們自己寫的項目模塊和工具類)的插件和運行平臺(一些依賴jar的配置)的插件,兩者結合啟動我們的項目就會正常運行。

我們啟動項目之後,在控制臺輸入指令“ss”,就會出現我們所有加載的插件的運行情况:

https://img-blog.csdn.net/20160310173922491?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
一啟動的時候,它會首先加載Eclipse的OSGI插件(Eclipse本身也是一種OSGI的容器):

我們打開我們的Eclipse安裝目錄,然後找到plugins文件夾,可以看到Eclipse所有的插件:

https://img-blog.csdn.net/20160310173950670?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

可以看到有文件夾形式的,有jar形式的插件。

我們怎麼去理解插件呢?
插件其實就是被開發工具或OSGI容器管理和配置起來的jar包。  

我們隨便打開一個文件夾類型的插件,可以看到:

https://img-blog.csdn.net/20160310174004498?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

可以看到裏面除了lib之外還有其它東西,然後有一個“OSGI-INF”文件夾。且不管它是什麼,這都足以說明我們的Eclipse就是一個OSGI容器。

(4)熱部署和熱啟動

我們接下來回到重點,在我們啟動的過程中,我們不停止運行,然後去停掉其中的一個模塊:

假如我們要停掉“管理”模塊:

https://img-blog.csdn.net/20160310174017304?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

也就是停掉id為22的插件

結果:

https://img-blog.csdn.net/20160310174029225?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
然後刷新我們的網站主頁面:

https://img-blog.csdn.net/20160310174125101?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

發現我們的“管理”模塊消失了!

這個模塊的消失並不是javascript的技術,而是一種服務器技術,我們是通過服務器內部把它動態卸載掉的。

我們的管理模塊去掉之後,網站的其它功能不受任何影響。至此我們的服務器沒有進行任何的暫停或關閉。

我們再停掉“購物車”模塊:

https://img-blog.csdn.net/20160310174849760?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

效果:

https://img-blog.csdn.net/20160310174743931?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

其它模塊依舊不受影響。

我們關閉了兩個模塊,現在輸入ss看一下所有插件和模塊的運行情况:

https://img-blog.csdn.net/20160310174908917?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

可以看到我們的兩個模塊處於RESOLVED狀態,也就是待解决狀態。

https://img-blog.csdn.net/20160310174936589?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center

當然我們也可以將我們的模塊在服務器開啟狀態下部署上去:
如我們啟動購物車模塊:

https://img-blog.csdn.net/20160310174936589?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
發現購物車回來了:

https://img-blog.csdn.net/20160310174946605?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
這就是所謂的熱部署,即是這個項目把它放在Web容器中之後,我們可以將某些功能給它拿下來,而且拿下來的時候不會對其他模塊造成影響。


通過購物網站這個項目讓大家真實的感受一下OSGI這個技術在項目開發和管理的一些强大的功能。
————————————————
版權聲明:本文為CSDN博主「荷葉清泉」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/heyeqingquan/article/details/86491707

 

原网站

版权声明
本文为[morpheusWB]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/186/202207050509477749.html