日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

OSGi-入门篇之服务层(03)

發(fā)布時(shí)間:2025/3/20 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OSGi-入门篇之服务层(03) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

作為OSGi框架中最上面的一層,服務(wù)層帶給了我們更多的動(dòng)態(tài)性,并且使用了大家或多或少都曾了解過的面向服務(wù)編程模型,其好處是顯而易見的。

1 什么是服務(wù)

簡(jiǎn)單的說,服務(wù)就是“為別人所做的工作”,比如兩個(gè)對(duì)象互相調(diào)用方法,那么被調(diào)用者就是在為調(diào)用者做工作。

那么如何將服務(wù)和一次普通的方法調(diào)用區(qū)別開來呢?其實(shí)一個(gè)服務(wù)可以看作是在服務(wù)的提供者和使用者之間的一個(gè)契約。使用者一般不關(guān)心其實(shí)現(xiàn)的細(xì)節(jié),甚至連誰提供的都不想知道,只要滿足這個(gè)契約(服務(wù)應(yīng)該提供什么功能,滿足什么格式)就好了。使用服務(wù)的過程也包含了發(fā)現(xiàn)服務(wù)和達(dá)成協(xié)議的形式,也就是說我們需要通過服務(wù)的標(biāo)志性特征來找到對(duì)應(yīng)的服務(wù)。

其實(shí),Java的接口可以說提供了一種契約的提供方式,我們能通過修改classpath來替換接口的不同的具體實(shí)現(xiàn)。但是OSGi能夠?yàn)檎业椒?wù)提供更加高層的抽象并且在應(yīng)用的執(zhí)行時(shí)動(dòng)態(tài)替換服務(wù)的實(shí)現(xiàn),這些特性在稍后將會(huì)提到。

2 為什么要使用服務(wù)

服務(wù)(更準(zhǔn)確的說是面向服務(wù)的編程模型)給予了我們一種即插即用的軟件開發(fā)方法,意味著更強(qiáng)的靈活性。這種靈活性是如何體現(xiàn)的呢?

  • 低耦合,利于組件復(fù)用:通過服務(wù)我們能夠清晰的定義組件的邊界,從而將服務(wù)的使用者和提供者之間的耦合度降到很低。
  • 更加強(qiáng)調(diào)接口而不是在具體的實(shí)現(xiàn):Java的interface提供了一種形式的契約,在OSGi的服務(wù)層中充分利用了接口特性的優(yōu)勢(shì),這樣使得無論有多少個(gè)類實(shí)現(xiàn)了這個(gè)接口,只要滿足對(duì)這個(gè)接口的功能需要,就可以被使用者使用。
  • 對(duì)于依賴有比較清晰地描述:單是接口本身只包含服務(wù)的名稱和參數(shù)類型,并不足以清楚的描述服務(wù)的所有特征,而面向服務(wù)的編程模型中要求了更加清晰的描述使得這些特征能夠唯一標(biāo)識(shí)一個(gè)服務(wù)。
  • 支持對(duì)多個(gè)競(jìng)爭(zhēng)實(shí)現(xiàn)(多個(gè)實(shí)現(xiàn)同一個(gè)接口的類)的篩選:服務(wù)框架會(huì)幫助你記錄服務(wù)的元數(shù)據(jù),可以據(jù)此幫助使用者查詢和篩選服務(wù),使用者更加的主動(dòng),這一點(diǎn)和傳統(tǒng)的依賴注入框架不同。

    3 什么時(shí)候使用/不使用服務(wù)

  • 可以考慮使用的時(shí)候:?
    當(dāng)你常常想要對(duì)主要的組件進(jìn)行替換和升級(jí)而不想重寫應(yīng)用的其他部分,或者當(dāng)你在程序中想要查找和選擇不同的接口實(shí)現(xiàn)的時(shí)候。

  • 不應(yīng)該使用的時(shí)候:?
    服務(wù)的加入和維護(hù)往往持續(xù)的增加框架的開銷,所以當(dāng)你開發(fā)的代碼對(duì)性能需求敏感的時(shí)候,不要使用服務(wù)。?
    顯然它也不應(yīng)該出現(xiàn)在兩段經(jīng)常一起開發(fā)和更新的緊耦合代碼之間,除非你真的需要在自己寫的代碼中得到“一個(gè)接口多個(gè)實(shí)現(xiàn)”的選擇權(quán)。

最后,如果不確定是否應(yīng)該使用服務(wù),可以先用面向接口的方式實(shí)現(xiàn),這至少是和使用服務(wù)很接近了,并且它也能簡(jiǎn)化你的開發(fā)。如果哪天你下定決心想把他們移植到服務(wù)層了,在面向接口的基礎(chǔ)上這個(gè)一直工作也會(huì)變得非常容易。

4 OSGi服務(wù)層基礎(chǔ)

首先,需要說明的是,OSGi的服務(wù)層除開前面提到的面向服務(wù)的編程模型,還有一個(gè)區(qū)別于其他很多類似模型的特性,那就是服務(wù)的完全動(dòng)態(tài)性。也就是說,當(dāng)一個(gè)bundle發(fā)現(xiàn)并開始使用OSGi中的一個(gè)服務(wù)了以后,這個(gè)服務(wù)可能在任何的時(shí)候改變或者是消失。這方面的內(nèi)容將在以后更加深入的講解。?
OSGi框架有一個(gè)中心化的注冊(cè)表,這個(gè)注冊(cè)表遵從publish-find-bind模型

一個(gè)提供服務(wù)的bundle可以發(fā)布POJO作為服務(wù)的實(shí)體;一個(gè)使用服務(wù)的bundle可以通過這個(gè)注冊(cè)表找到和綁定服務(wù)。?
我們可以通過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)throwsInvalidSyntaxException;

  
ServiceReference getServiceReference(String clazz);

  Object getService(ServiceReference reference);

  
boolean ungetService(ServiceReference reference);

...}

4.1 發(fā)布服務(wù)

為了讓別的bundle能發(fā)現(xiàn)這個(gè)服務(wù),你必須在發(fā)布它之前對(duì)其進(jìn)行特征描述。這些特征包括接口的名字(可以是名字的數(shù)組),接口的實(shí)現(xiàn),和一個(gè)可選的java.util.Dictionary類型的元數(shù)據(jù)信息。下面是一個(gè)例子:

String[] interfaces =newString[]{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對(duì)象,我們可以用這個(gè)對(duì)象來更新服務(wù)的元數(shù)據(jù):?
registration.setProperties(newMetadata);

也可以直接就把這個(gè)服務(wù)移除:?
registration.unregister();

需要注意的是這個(gè)對(duì)象不能和其他Bundles共享,因?yàn)樗桶l(fā)布服務(wù)的bundle的生命周期相互依存,也就是說,如果這個(gè)bundle已經(jīng)不在框架執(zhí)行環(huán)境中存在,那么這個(gè)對(duì)象也不應(yīng)該存在了,“皮之不存毛將焉附”就是這個(gè)道理。

試想如果這個(gè)ServiceRegistration共享給了其他的bundle(具體的說就是其他bundle中存在對(duì)這個(gè)對(duì)象的引用),那么發(fā)布服務(wù)的那個(gè)bundle即使被移除了,由于其他bundle中的引用依然存在,那么垃圾處理機(jī)制不會(huì)抹去這個(gè)對(duì)象,這樣不但于理不合,而且實(shí)際上這個(gè)對(duì)象也是不可用的,因?yàn)檫@個(gè)對(duì)象所依存的bundle已經(jīng)不在了。

碼中的參數(shù)new LSE()是一個(gè)POJO,這個(gè)對(duì)象不需要實(shí)現(xiàn)任何OSGi類型或者使用標(biāo)注,只要滿足服務(wù)約定(這里就是接口)就可以了。

此外,如果在刪除發(fā)布的服務(wù)之前bundle停止了,框架會(huì)幫助你刪除這些服務(wù)。

4.2 發(fā)現(xiàn)和綁定服務(wù)

上一小節(jié)我們說明了如何描述和發(fā)布一個(gè)服務(wù),那么現(xiàn)在我們可以根據(jù)服務(wù)約定從注冊(cè)表中找到正確的服務(wù)。?
下面是發(fā)現(xiàn)服務(wù)并獲得其引用的接口:

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

這是根據(jù)實(shí)現(xiàn)的接口名稱獲得的服務(wù),也是最簡(jiǎn)單的方法。

注意這里的reference是服務(wù)對(duì)象的間接引用,可是為什么要用間接引用而不直接返回那個(gè)實(shí)際的服務(wù)對(duì)象呢?實(shí)際上是為了將服務(wù)的使用和服務(wù)的實(shí)現(xiàn)進(jìn)行解耦,將服務(wù)注冊(cè)表作為兩者的中間人,達(dá)到跟蹤和控制服務(wù)的目的,同時(shí)還可以在服務(wù)消失了以后通知使用者。

這個(gè)方法的返回類型是ServiceReference,它可以在bundle之間互享,因?yàn)樗褪褂梅?wù)的bundle的生命周期無關(guān)。

4.2.1 選擇最適合你的服務(wù)

在getServiceReference這個(gè)方法中,選擇service的默認(rèn)優(yōu)先級(jí)是先選擇service.rank最高的,在rank相等的情況下選擇最早在框架中注冊(cè)的。除了這個(gè)默認(rèn)的規(guī)則,我們還可以在 getServiceReferences中通過添加過濾參數(shù)(作為調(diào)用該方法的第二個(gè)參數(shù))來做一些篩選。

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

在這里的匹配參數(shù)是一個(gè)字符串,這個(gè)字符串的格式屬于LDAP查詢格式,在RFC 1960標(biāo)準(zhǔn)中有完整的描述。

上面的字符串中等號(hào)左邊的內(nèi)容就是前面提到的元數(shù)據(jù)(Dictionary)中的左值,通過這個(gè)左值對(duì)應(yīng)的右值來與服務(wù)所帶有的元數(shù)據(jù)進(jìn)行匹配。一些簡(jiǎn)單的匹配示例如下:?
屬性匹配:?
(name=John Smith)?
(age>=20)?
(age<=65)?
模糊匹配:?
(name~=johnsmith)?
通配符匹配:?
(name=Jo*n*Smith*)?
判斷某個(gè)屬性是否存在:?
(name=)?
條件與:?
(&(name=John Smith)(occupation=doctor))?
條件或:?
(|(name~=John Smith)(name~=Smith John))?
*條件非: **?
(!(name=John Smith))

4.2.2 綁定和使用服務(wù)

在你發(fā)現(xiàn)了服務(wù)之后,使用服務(wù)之前,你必須從注冊(cè)表中綁定實(shí)現(xiàn)的服務(wù)。

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

這個(gè)方法返回的POJO實(shí)例和之前在注冊(cè)表中注冊(cè)的實(shí)例是同一個(gè)。

每次使用getService方法的時(shí)候,注冊(cè)表會(huì)將對(duì)應(yīng)服務(wù)的使用次數(shù)加1,同時(shí)會(huì)記錄誰在使用這個(gè)服務(wù)。所以當(dāng)你不在想使用這服務(wù)的時(shí)候,最好告訴注冊(cè)表一聲。

bundleContext.ungetService(reference); listing =null;

給出第二條語句的目的并不是為了通知注冊(cè)表,而是為了讓java的垃圾處理機(jī)制安全運(yùn)作。因?yàn)檫@里我們用了一個(gè)局部變量listing來作為服務(wù)對(duì)象的一個(gè)引用,(不妨假設(shè)listing是最后一個(gè)引用這個(gè)對(duì)象的變量),如果我們不設(shè)為null,那么在這個(gè)listing消亡之前,那個(gè)服務(wù)對(duì)象有可能不會(huì)被垃圾處理掉(即使在程序邏輯上這個(gè)服務(wù)對(duì)象已經(jīng)是“垃圾”了),這可能會(huì)引發(fā)一些問題。

不過,這種用局部變量引用服務(wù)對(duì)象的方式本來就不對(duì)。一般來說,還是應(yīng)該在每次需要使用的時(shí)候臨時(shí)從ServiceReference獲得,并且要考慮到這個(gè)服務(wù)在任何時(shí)候都有可能消亡。

總結(jié)

以上是生活随笔為你收集整理的OSGi-入门篇之服务层(03)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。