Liferay DXP 中 OSGi註解(Annotation) - 使用手冊(譯文)

原文地址:liferayee.com

當你查看Liferay 7 CE/Liferay DXP源碼時,你會看到大量不同的註解。當你除此看到這些註解的時候,可能會感覺到有些無所適從。所以我想寫一些引用指導來解釋這些註解是什麼,並且在你自己的OSGi代碼中什麼時候使用比較合適。
 
我們開始吧...
 
@Component
 
在OSGi世界中,這種註解用於定義“聲明式服務(Declarative Service)”。聲明式服務是OSGi中用於動態生成服務。它使容器中的大量的組件(component)間可以互相關聯調用。
 
在Component註解中有三個組要屬性:
  • immediate - 通常設置爲true。用於使組件在部署後直接啓動(start),不用等待其他引用或者使用懶加載(lazy startup)。
  • properties - 用設置一系列OSGi屬性綁定到組件。這些屬性對於當前組件是可見的,但是更重要的是它們對於其他組件也是可見的。這些組件可以幫助配置組件,也可以用於支持組件過濾。
  • service - 定義組件實現的服務。有時這個屬性是可選的,但是通常是必要的,因爲這樣可以使組件要實現服務更明確。service的值通常是一個接口,但是也可以使用一個實體類。
什麼時候需要使用@Component?只要需要在OSGi容器中使用要發佈一個組件的時候,就可以使用。不是所有的類都需要是組件。只是當你需要將一個插件植入到Liferay環境中的時候纔會聲明一個組件。(例如,添加一個導航項、定義一個MVC command handler、覆寫一個Liferay組件或者爲自己的擴展框架寫一個插件)。
 
@Reference
 
這個註解是和@Component相對應的。@Reference用於獲取OSGi容器中其他的組件並且注入到你自己的組件中。需要注意的是,因爲OSGi在做注入的工作,你只能注入OSGi組件類。@Reference註解會被非組件類忽略,並且也會組件的被子類忽略。對於所有的引用注入,都必須在@Component類中聲明。
當你定義一個基類時注入了一些其他的服務,並且這個基類沒有使用@Component註解(因爲定義時還沒有完成),@Reference註解會被非組件類忽略,這樣的情況下,所有的注入都是無效的。最終,所有的setter方法和@Reference註解都需要複製到實習子類中。這樣顯得相當冗餘。需要注意這一點。
可能@Reference最常見的屬性是“unbind”屬性了。在setter方法中,經常會看見@Reference(unbine="-")這樣的寫法。當你爲setter方法使用@Reference時,OSGi使用使用這個setter方法作爲組件的setter方法。unbind屬性指定了當組件釋放是,不需要調用任何其他的方法。也就是說,不需要爲組件釋放做任何的處理。多數情況下,這麼做沒有問題,服務器啓動,OSGi綁定組件,並且使用組件一直到系統關閉。
另外一個屬性是“target”,Target用於支持過濾。還記得@Component中的屬性吧?通過使用target屬性,可以通過特定查詢語法指定一類組件。
例如:
@Reference( target = "(javax.portlet.name=" + NotificationsPortletKeys.NOTIFICATIONS + ")", unbind = "-" ) 

protected void setPanelApp(PanelApp panelApp) { 
    _panelApp = panelApp; 
}
這段代碼希望獲取PanelApp組件的實例,但是它卻指定了Notification portlet中的PanelApp組件。所以其他portlet中的PanelApp不符合條件,只有Notification portlet中的PanelApp才符合條件。
我們這裏討論的屬性有時候會很重要,所以我會詳細討論這些屬性。
Cardinality
第一個是cardinality屬性,默認值是ReferenceCardinality.MANDITORY,可選值有:OPTIONAL、MULTIPLE和AT_LEAST_ONE。這些值的意義是:
  • MANDITORY - 引用必須在組件啓動前可用並且注入。
  • OPTIONAL - 引用在組件啓動階段時不是必要的,在不指定組件的時候也是可以正常工作的。
  • MULTIPLE - 有多種不同的資源可以引用,組件會使用全部的方法。和OPTIONAL相似,在組件啓動階段,引用不是不要的。
  • AT_LEAST_ONE - 多種資源可以滿足引用,組件會全部使用。但是在組件啓動時,至少有一個資源是可用的。
Multiple選項可以讓你通過引用,使用多種符合條件的方法調用。這種方式,只有在使用setter方法中@Reference註解,並且setter方法是向列表或者數組中添加內容的情況下才合理。或者可以使用ServiceTracker替換這種寫法,這樣你就不用親自手動去管理列表了。
 
Opional可以使組件在啓動時不必須指定某個引用。在有循環引用問題時,這種方法就會幫助你解決問題。例如:A引用B,B引用C,C引用A。如果這三個組件都設置引用爲REQUIRED,所有組件都不會啓動,因爲它們都在等待其他組件滿足條件(只有已經啓動的組件才能指派給@Reference)。爲引用使用optional選項就打破了這種循環,組件就可以正常啓動,並且引用問題也解決了。
 
Policy
下一個重要的@Reference屬性是policy。Policy的值可以是ReferencePolicy.STATIC (默認)或者ReferencePolicy.DYNAMIC。他們的意義是:
  • STATIC - 只有在已經指派了引用之後,組件才能啓動。在啓動之後,當出現其他可用引用時,組件也不會理會。
  • DYNAMIC - 無論有沒有可用的引用,組件都會啓動。並且在有新的可用引用的時候,組件會使用新的引用。
引用策略控制着在組件啓動後,當新的引用資源可用的時候的使用策略。總體來說,使用STATIC,組件會忽略新的引用,使用DYNAMIC,當出現新的可用引用時,組件會改變引用。
 
PolicyOption
和policy一同使用的另外一個屬性是policyOption。這個屬性的值可以是ReferencePolicyOption.RELUCTANT (默認) 或者 ReferencePolicyOption.GREEDY。他們的意思是:
  • RELUCTANT - 對於單一(single)的引用,新的可用引用會被忽略。對於多項(multiple)引用,新的可用引用出現的時候會被綁定。
  • GREEDY - 只要新的可用引用出現的時候,組件就會綁定這些引用。.
 
這些選項組合起來有很多種方式。
首先是默認的,ReferenceCardinality.MANDITORY + ReferencePolicy.STATIC + ReferencePolicyOption.RELUCTANT。
這樣組合是組件一定要一個引用可用才能啓動,並且忽略新的可用引用。這樣的默認組和確保了組件的穩定性。
另外一種組合是ReferenceCardinality.OPTIONAL/MULTIPLE + ReferencePolicy.DYNAMIC + ReferencePolicyOption.GREEDY。
在這樣的配置中,組件在缺少服務引用的時候也會正常使用,但是組件在使用過程中允許新的引用和引用變更,在新的引用可用時,組件會主動的綁定引用服務。
也會有其他的組合,但是在設置的時候需要了解這樣的設置對組件的影響。畢竟在你聲明引用的時候你要了解如何是組件工作。你需要考慮到組件在沒有引用的時候如何響應,如果服務不可用的時候組,件是否需要停止工作。在你考慮問題的時候不信要考慮理想情況,也要考慮到特殊情況,例如重新部署、卸載、服務差距和組件的容錯性。如果這些問題都能解決,那麼組件就是比較理想的。
最後,回想一下,什麼時候需要使用@Reference註解?就是你需要從OSGi環境中向你的組件注入服務的時候。這些服務可以是你自己的服務,也可以是OSGi容器中其他模塊的服務。記住,@Reference只能在OSGi組件中使用,但是你可以使你的類通過@Component註解變成組件。
 
@BeanReference
這個是一個Liferay註解,用於向Spring bean中注入一個Liferay core中的引用。
 
@ServiceReference
這是一個Liferay註解,用於向組件中注入一個引用,這個引用是來自於Spring Extender module bean。
 
稍等!三種引用註解?我需要用哪一種?
我們來解釋一下這三種引用註解。根據我的經驗來判斷,多數情況下,你需要使用@Reference註解。Liferay core Spring beans和Spring Extender module beans同樣也是暴露在OSGi容器中的,所以@Reference在多數情況下是沒問題的。
如果在使用@Reference的時候,服務沒有被注入,並且返回null,這就意味着你可能需要使用其他的引用註解了。選擇哪種註解原則不難:如果bean是在Liferay core中,就是用@BeanReference;反之,但是如果是在Spring Extender module中,就是用@ServiceReference註解。注意,無論是bean或者service註解都會要求你的組件使用Spring Extender。如何引用依賴,參考任何使用ServiceBuilder的服務模塊,查看build.gradle和bnd.bnd,用同樣的方法修改你自己的模塊。
 
@Activate
@Activate註解是對應Spring的InitializingBean接口。它聲明瞭組件在啓動是要調用的方法。在Liferay源碼中,你會看到它會被三個主要的方法使用:
@Activate 
protected void activate() { 
    ... 
} 

@Activate 
protected void activate(Map<String, Object> properties) {
     ... 
} 

@Activate 
protected void activate(BundleContext bundleContext, Map<String, Object> properties) {
    ... 
} 
也有其他的方法使用這個註解,只要在源碼中搜索@Activate就可以找到很多不同的用例了。除了無參數的activate方法,其他的都依賴於OSGi所注入的值。注意properties映射實際是從你的OSGi的Configuration Admin服務取得的。
 
什麼時候需要使用@Activate呢?只要你需要在組件啓動後、被使用前需要做一些初始化操作的時候就可以使用。例如,我曾經設置Quartz計劃任務,驗證數據庫實體的時候使用過。
 
@Deactivate
@Dactivate註解是和@Ativate註解相反的,它定義了組件在停用是需要調用的方法。
 
@Modified
@Modified註解定義了當組件被改變時需要調用的方法。特別是在標記@Reference了的方法被改變的時候。在Liferay源碼中,@Modified註解經常是和標記@Activate的方法綁定在一起的,這樣同一個方法就能同時處理啓動和改變了。
 
@ProviderType
@ProviderType是BND中使用的,通常是在考慮將head包含的複雜情況時使用。長話短說,@ProviderType是被BND使用,用來定義在實現類中可以指派的OSGi manifest的版本。並且它嘗試限制組件版本範圍的使用。
 
它的重點是用來確定接口的改變,通過爲實現類限制的版本,會強制實現類來根據接口來進行更新。
 
什麼時候使用@ProviderType呢?其實,並不一定需要使用。你可以看見,在ServiceBuilder生成的代碼裏面已經被使用了。我在這裏提到這個註解的原因並不是因爲你一定要使用它,而是滿足你的好奇心。
 
@ImplementationClassName
這個是Liferay中爲ServiceBuilder實體接口使用的註解。它定義了在service module中用來實現接口的類。
這個你並以需要使用,只是爲了讓你知道它是做什麼的的。
 
@Transactional
這是另一個ServiceBuilder服務接口使用的註解。它定義了服務方法的事務需求。
這個方法在你開發的時候也用不到。
 
@Indexable
@Indexable用來修飾會使搜索索引更新你的方法,特別是ServiceBuilder中的add、update和delete實體的方法。
你可以爲你的實現增、刪、改的方法使用@Indexable註解。如果實體關聯com.liferay.portal.kernel.search.Indexer 相關的實現方法,那麼實體就可以被索引。
 
@SystemEvent
@SystemEvent註解是在ServiceBuilder生成的代碼中被可能生成系統事件的方法使用的。系統事件和staging、LAR和導入導出處理等相關。例如,當一個web conteng被刪除,這個就會生成一個SystemEvent記錄。在staging環境中,當“Publish to Live”原型是,刪除SystemEvent確保了相關聯的web content在live站點中也被刪除。
需要什麼時候使用@SystemEvent註解呢?說實話,我也不知道。在我10年的經驗中,我從來沒有需要生成SystemEvent記錄的時候,也從來沒更改過Liferay發佈和LAR處理。如果任何人有相關經驗使用@SystemEvent註解的話,我很願意側耳恭聽。
 
@Meta
OSGi有一個基於XML的系統用於爲Configuration Admin定義配置詳細信息。BND項目的通過使用@Meta註解,可以使BND根據配置接口中使用這個註解的方法生成配置文件。
 
重要提示:一定要在bnd.bnd文件中添加下行代碼才能使用@Meta註解:
-metatype: *
如果沒添加的話,在生成XML配置文件的時候,是不會用到@Meta註解的。
 
@Meta.OCD
這個註解用於“Object Class Definition”方面的配置詳細信息。這個註解用於爲在接口層爲類的定義提供id,名字和語言等詳細信息。
什麼時候使用這個註解呢?當爲組件定義一個Configuration Admin接口時,並且這個接口是在Control Panel -> System Setting有一個配置選項的時候使用。
 
注意@Meta.OCD屬性包含了本地化的設置。這樣就可以使用resource bundle來翻譯配置名稱、詳細配置信息和@ExtendedObjectClassDefinition分類了。
 
@Meta.AD
這個註解用於“Attribute Definition”,用於定義配置中表單項層面的定義。註解用於爲表單項提供ID、名稱、說明、默認值和其他詳細信息。
 
什麼時候使用這個註解?就是在需要爲System Setting中的配置提供更多關於配置項目細節的時候使用。
 
@ExtendedObjectClassDefinition
這個是Liferay的註解,用於定義配置的類型(就是在System Setting中上面顯示的那些分類)和配置的範圍。
 
範圍可以是下面的值:
  • SYSTEM - 整個系統中的全局配置,在生個系統中只有一個配置。
  • COMPANY - Company級,每個portal實例中只可以有一個配置。
  • GROUP - Group(站點)級,每個站點中只可以有一個配置。
  • PORTLET_INSTANCE - 這個和portlet實例參數類似,每個portlet實例中都會有一分配置。
什麼時候要用到這個註解呢?每次用到@Meta.OCD註解的時候,都需要用到一次@ExtendedObjectClassDefinition註解來定義配置的分類。
 
@OSGiBeanProperties
這是一個Liferay的註解,用來定義OSGi組件屬性,這些屬性用來註冊Spring bean爲一個OSGi組件。你會在ServiceBuilder模塊中見到這個註解被使用,將Spring bean暴露給OSGi容器。記着,ServiceBuilder依然是基於Spring的(SpringExtender),這個註解就是用來將Spring bean註冊爲OSGi組件用的。
 
什麼時候會用到這個註解呢?如果你在你自己的模塊中使用了Spring Extender來使用Spring,並且你想將Spring bean在OSGi容器中暴露爲組件(這麼做可以使其他模塊使用這個Spring bean),這個時候你就需要使用這個註解了。
 
我沒有在這裏討論更多的細節,因爲這個註解可以在javadoc中被大量的找到。查看這個例子: https://github.com/liferay/liferay-portal/blob/master/portal-kernel/src/com/liferay/portal/kernel/spring/osgi/OSGiBeanProperties.java
 
總結
 
這就是我在Liferay 7 CE / Liferay DXP中見到的所有註解了。希望這些信息可以幫到你的Liferay開發。如果你發現本文沒有涉及的註解或者希望瞭解更多細節,請隨時向原文博客或者在這裏提問。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章