【前編】OSGiアノテーションとは?その紹介と活用法 - 【前編】OSGiアノテーションとは?その紹介と活用法 - Japan

null 【前編】OSGiアノテーションとは?その紹介と活用法
by Yasuyuki Takeo

【前編】OSGiアノテーションとは?その紹介と活用法

画像

こんにちは。
日本ライフレイ、サポータビリティエンジニアの竹生(たけお)です。

ライフレイデベロッパーに向けた情報発信を強化するべく、ライフレイ開発に役立つ記事を今後定期的にコーポレートブログにて投稿していくこととなりました。

主にライフレイのコミュニティサイトへ投稿されている有益な情報を、日本語へ翻訳していくかたちとなりますが、「ライフレイ開発にまつわるこういう記事が読みたい」「ライフレイ開発でここがわからない!」などリクエストございましたら、お気軽にyasuyuki.takeo@liferay.comまでお気軽にご連絡ください!

また、これまで日本語で発信してきたコンテンツも、Qiitaでお読みいただけます。よろしければそちらの記事も合わせてお役立てください。
https://qiita.com/yasuflatland-lf

はじめに:テクニカル記事ごとに必要な知識レベルについて

今後、テクニカルな記事を投稿していくにあたって、記事の冒頭には毎回以下を明記するようにします。

  • 1. Liferay開発について、どの程度の知識レベルが必要なコンテンツか?
  • 2. どういったユースケースに役立つコンテンツか?

1.の知識レベルに関しては、初級・中級・上級にわけ、それぞれ以下のように定義しています。

  •  初級:誰かの助けを得ながらLiferay開発ができる方
  •  中級:自分1人でLiferay開発ができる方
  •  上級:中級、初級レベルの人を教えながら、複数人の開発をリードする方

ぜひ読む際はご参照ください。

 

さて、前置きが長くなってしまいましたが、第一回目投稿は、OSGiアノテーションの紹介と利用どころに関する記事をご紹介します。

本記事は以下の知識レベルの方たち、ユースケースが対象です。


■ 本記事対象の知識レベルとユースケース

  • 中級レベル
  • 一般的なLiferay DXP開発に必須

OSGiアノテーションを一つ一つしっかり紹介していくため、日本語版では、前編と後編に記事を分けてお届けしたいと思います。なのでまずは前編です。

後編は、数日後にアップ予定です。

Liferay 7CEやLiferay DXPコードのレビューをしていると、さまざまな種類のアノテーションを目にする機会があるかと思います。初めてそれらを見ると、その種類の多さに圧倒されてしまう方も多いのではないでしょうか。そこで本記事では、代表的なアノテーションの説明と、それらをいつOSGiコードで使用するか、簡単にまとめてご紹介します。

それでは早速みてみましょう。

 

@Component

※本記事はLiferay Community Blogに投稿されている”Liferay/OSGi Annotations - What they are and when to use them”を翻訳したものです。配信元または著者の許可を得て配信しています。
※オリジナルの英記事著者:David H Nebinger サウスカロライナ州のサマーヴィルに住むLiferay, Inc.のSoftware Architect/リードコンサルタント。Liferay Communityサイトにて、多くの技術情報を投稿するなど精力的に活動しています。

OSGiの世界で、@Componentはサービス実装を定義する重要な「宣言型サービス」アノテーションです。「宣言型サービス(DS)」とは、サービスを動的に宣言するためのOSGiの一面であり、他のコンポーネント同士を繋げられるようにするものです。

このアノテーションには、主に以下3つの属性があります。

  • immediate - 基本はtrueに設定します。これによりコンポーネントがすぐに開始され、参照の解決や遅延起動を待つ必要がなくなります。
  • properties - コンポーネントにバインドするOSGiプロパティのセットを渡すために使用されます。該当のコンポーネントはもちろん、他のコンポーネントもプロパティを見ることができるようになります。これらのプロパティはコンポーネントの設定に役立ちますが、コンポーネントのフィルタリングの際にも役立ちます。
  • service - コンポーネントが実装するサービスを定義します。場合によってはオプション(必須ではない)ですが、コンポーネントのサービス内容の曖昧さ回避するために義務付けられていることもよくあります。リスト化されたサービスは、多くの場合インターフェースですが、サービスに具体的なクラスを使用することもできます。

@Componentはいつ使うか?
OSGiコンテナに公開する必要があるコンポーネントを作成するときに利用します。クラスのすべてがコンポーネントである必要はありません。コードをLiferay環境へプラグインする場合や(製品ナビゲーションメニューを追加する、MVCコマンドハンドラを定義する、Liferayコンポーネントをオーバーライドする時など)、独自の拡張フレームワークへプラグインする必要があるときにコンポーネントを宣言します。

 

@Reference

@Referenceは@Componentアノテーションに対応するものです。
@Referenceは、OSGiがコンポーネント参照をコンポーネントへ注入する際に使用し、OSGi @Componentクラスでのみ動作します。@Referenceアノテーションは、非コンポーネントやサブクラスでは無視されます。必要な参照の注入は、まず@Componentクラス自体で行われている必要があります。

これは、注入された多数のサービスにおいて基本クラスを定義したいときに役立ちます。基本クラスは@Componentアノテーションを取得せず、@Referenceアノテーションはコンポーネント以外のクラスでは無視されるため、注入は行われません。またコンポーネント以外のクラスでも@Referenceアノテーションは無視されるため、注入されません。すべてのsetterと、すべての具体的サブクラスへの@Referenceアノテーションをコピーすることになります。手間はありますが、必要な作業になります。

おそらく最もよく使われている属性は、”unbind”属性です。setterメソッドで@Reference(unbind = " - ")の形で検索されることがよくあります。 @Referenceでsetterメソッドを使用すると、OSGiは使用するコンポーネントとともにsetterを呼び出しますが、unbind属性はコンポーネントがバインドされていないときに呼び出すメソッドがないことを示します。したがって、基本的には扱う必要のないコンポーネントです。ほとんどの場合、サーバーが起動しすれば、OSGiがコンポーネントをバインドし、システムがシャットダウンするまでうまく使用することができるはずです。

ここで見られる別の属性は”target”属性です。Targetはフィルタメカニズムとして使用されます。
@Componentで扱うプロパティを覚えていますか? target属性では、受け取るコンポーネントのインスタンスを、より具体的に識別するクエリを指定できます。

例:


@Reference(
  target = "(javax.portlet.name=" + NotificationsPortletKeys.NOTIFICATIONS + ")",
  unbind = "-"
)
protected void setPanelApp(PanelApp panelApp) {
  _panelApp = panelApp;
}

 

上記は、PanelAppコンポーネントのインスタンスを取得したいときのコードで、特に、通知ポートレットに関連付けられたPanelAppコンポーネントを探しています。他のPanelAppコンポーネントはこのフィルタと一致せず、適用されません。

他の重要な属性としては、Cardinality属性があります。デフォルト値はReferenceCardinality.MANDITORYですが、その他の値は、OPTIONAL, MULTIPL, およびAT_LEAST_ONEです。これらの意味は以下の通りです:

  • MANDITORY - コンポーネントを開始する前に、参照を可能にし、注入する必要がある。
  • OPTIONAL - コンポーネントが開始するための参照は必要ではなく、コンポーネント割り当てがなくても機能する
  • MULTIPLE - 複数のリソースが参照を満たしている可能性があり、コンポーネントはそれらをすべて使用するが、OPTIONAL同様コンポーネントを開始するための参照は不要。
  • AT_LEAST_ONE - 複数のリソースが参照を満たしている可能性があり、コンポーネントはそれらをすべて使用するが、コンポーネントが開始するためには、少なくとも1つは確実に参照可能であること。

複数のオプションを使用すると、一致する参照を含む複数の呼び出しを取得できます。これは、Setterメソッドで@Referenceアノテーションを使用していて、メソッド内でリストまたは配列に追加しようとしていた場合にのみ意味があります。また、このような処理の代わりにServiceTrackerを使用すると、自分でコンポーネントのリストを管理する必要がなくなります。

Optionalを使用すると、割り当てられた参照なしにコンポーネントを起動することができます。これは次のような循環参照の問題があるシナリオの場合に役立ちます: Aを参照するCを参照するBをAが参照する場合。この3つすべてにREQUIREDを使用する場合、参照条件が満たされないため開始されません(開始コンポーネントのみ参照として割り当てられる)。 1つのコンポーネントで参照をoptionalとして扱うことでこの循環を分割することができます。これによって開始することができ、参照は解決されます。

次に重要な@Reference属性は”policy”です。 Policyは、ReferencePolicy.STATIC(デフォルト)またはReferencePolicy.DYNAMICのいずれかです。これらの意味は以下の通りです:

  • STATIC - コンポーネントは、割り当てられた参照がある場合にのみ起動され、代替サービスが利用可能になると通知される。
  • DYNAMIC - 参照がある場合もそうでない場合でもコンポーネントが開始され、新しい参照が利用可能になった場合もコンポーネントは受け入れる。

参照ポリシーは、新しい参照オプションが利用可能になったときに、コンポーネントが起動後の処理を制御します。
STATICの場合、新しい参照オプションは無視され、DYNAMICの場合コンポーネントが処理を変更します。

policyに加えて、もう一つの重要な@Reference属性は”policyOption”です。この属性は、ReferencePolicyOption.RELUCTANT(デフォルト)またはReferencePolicyOption.GREEDYのいずれかです。これらの意味は以下の通りです:

  • RELUCTANC - 単一参照Cardinalityの場合、使用可能になった新しい参照コンポーネントを無視する。複数の参照Cardinalityが設定されている場合、新しい参照可能コンポーネントがバインドされます。
  • GREEDY - 新しい参照コンポーネントが利用可能になった場合に、それらをバインドする。

選択肢が多くて混乱するかとおもいます...。
以下では、よく使われているグループ単位で説明します。

最初は、デフォルトのReferenceCardinality.MANDITORY、ReferencePolicy.STATICおよびReferencePolicyOption.RELUCTANTです。これは、コンポーネントが開始する参照サービスが1つだけでなければならないよう制御し、コンポーネントは新しいサービスが開始されても、これを無視します。これは優秀で正常なデフォルト設定であり、コンポーネントの安定性を確保します。

Liferayソースで一般的に使用されている別のグループは、ReferenceCardinality.OPTIONALまたはMULTIPLE、ReferencePolicy.DYNAMIC、およびReferencePolicyOption.GREEDYです。この構成では、コンポーネントは参照サービスの有無にかかわらず機能しますが、そのコンポーネントはその参照を変更/追加することを可能にし、新しい参照が利用可能となった時にそれらをバインドします。

他の組み合わせも可能ではありますが、コンポーネントへの影響を理解しておく必要があります。参照を宣言するということは、コンポーネントを完成させるために何らかのサービスが必要であると宣言しているのと同じことです。サービスが存在しないとき、そのコンポーネントはどう反応するのか、依存するサービスが利用できないためにコンポーネントが停止した場合にどうなるかなどを考慮してください。完璧な世界のシナリオだけでなく、再デプロイ、アンインストール、サービスギャップといった不具合を想定し、コンポーネントがそうした複雑な混乱をどのように乗り切ることができるかを確認します。様々な不具合乗り越えることに確信が持てれば、もう安心でしょう。

最後に、@Referenceアノテーションはいつ使うかですが、OSGi環境からコンポーネントへサービス注入が必要な場合に使います。注入は、自分のモジュールまたはOSGiコンテナ内の他のモジュールから行うことができます。 @ReferenceはOSGiコンポーネントでのみ機能しますが、@Componentリファレンスを追加することでコンポーネントへ変更できます。

 

@BeanReference

@BeanReferenceは、LiferayコアからSpring Beanへの参照を注入するために使用されるLiferayアノテーションです。

@ServiceReference

@ServiceReferenceは、Spring ExtenderモジュールBeanからの参照を注入するために使用されるLiferayアノテーションです。

3つの異なる参照アノテーション、どれを使うべきか?

3種類の参照アノテーションがあります。経験則からいうと、ほとんどの場合、@Referenceアノテーションだけを使います。 LiferayコアのSpring BeanとSpring ExtenderモジュールBeanも、OSGiコンポーネントとして公開されているため、@Referenceでほとんどの場合動作します。

@Referenceが注入されていない、またはnullの場合、他の参照アノテーションを1つ使用すべきであるというサインです。BeanがLiferayコアのBeanの場合は@BeanReferenceを使用し、Spring Extenderモジュールの場合は、@ServiceReferenceアノテーションを使用してください。Beanとサービスアノテーションの両方において、コンポーネントがSpring Extenderを使用している必要があることに注意してください。これを設定するには、ServiceBuilderサービスモジュールを調べて、build.gradleやbnd.bndファイルの更新方法などを確認してください。

 


前編はここまで

いかがでしたかでしょうか?
アノテーションの紹介、ひとまず前編はここまでです。他にも見ることがあるアノテーションの紹介は、後編へつづきます!

本記事のお問い合わせはこちらまで→yasuyuki.takeo@liferay.com

 


 

■ これまで発信してきた日本語による技術コンテンツを、Qiitaでお読みいただけます。よろしければそちらの記事も合わせてご役立てください。
https://qiita.com/yasuflatland-lf

Doorkeeperのライフレイコミュニティメンバー大歓迎!
コーポレートブログの技術コンテンツ更新情報など定期的におとどけしています。
https://liferay.doorkeeper.jp/

業界最高の評価

Gartner

Gartnerレポート:『リーダー』に位置づけ
ライフレイは9年連続で、Gartner社によるデジタルエクスペリエンスプラットフォーム(DXP )分野のマジック・クアドラントにおいて、リーダーに選出されました。

レポートの詳細、ダウンロード