1前書き

この記事では、

Google Guiceの基礎

について説明します。 Guiceで基本的な依存性注入(DI)タスクを完了するためのアプローチを見ていきます。

また、Guiceのアプローチと、Spring and Contexts and Dependency Injection(CDI)のようなより確立されたDIフレームワークのアプローチとを比較対照します。

この記事では、読者がリンクの基礎について理解していることを前提としています。


2セットアップ

MavenプロジェクトでGoogle Guiceを使用するには、

pom.xml

に次の依存関係を追加する必要があります。

<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>4.1.0</version>
</dependency>

Guiceエクステンションのコレクションもあります(それらについては後で説明します)。https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22com.google.inject.extensions%22% 20AND%20v%3A%224.1.0%22[ここ]、およびhttps://github.com/google/guice/wiki/3rdPartyModules[サードパーティ製のモジュールとして]Guiceの機能を拡張する(主に統合を提供することによって)より確立されたJavaフレームワークへ)。


3 Guice

による基本的な依存性注入


3.1. 私たちのサンプルアプリケーション

電子メール、SMS、およびIMの3つのヘルプデスクビジネスのコミュニケーション手段をサポートするクラスを設計するシナリオを進めます。

クラスを考えてみましょう:

public class Communication {

    @Inject
    private Logger logger;

    @Inject
    private Communicator communicator;

    public Communication(Boolean keepRecords) {
        if (keepRecords) {
            System.out.println("Message logging enabled");
        }
    }

    public boolean sendMessage(String message) {
        return communicator.sendMessage(message);
    }

}

このCommunicationクラスは、通信の基本単位です。このクラスのインスタンスは、利用可能な通信チャネルを介してメッセージを送信するために使用されます。上記のように、

Communication

には

Communicator

があり、これを使って実際のメッセージ送信を行います。

Guiceへの基本的な入り口は__Injectorです。

public static void main(String[]args){
    Injector injector = Guice.createInjector(new BasicModule());
    Communication comms = injector.getInstance(Communication.class);
}

このメインメソッドは

Communication

クラスのインスタンスを取得します。また、Guiceの基本概念である

Module

(この例では

BasicModule

を使用)も紹介します。


Module

はバインディングの定義の基本単位

(またはSpringで知られているように配線)です。

  • Guiceは依存性の注入と管理のためにコード優先のアプローチを採用しています** ので、多くのXMLをそのまま使用することはできません。

上記の例では、

Communication

の依存関係ツリーは、クラスにデフォルトの引数なしのコンストラクタがある場合、

just-in-time binding

と呼ばれる機能を使用して暗黙的に挿入されます。これは、Guiceの開始当初からの機能であり、v4.3以降のSpringでのみ利用可能です。


3.2. Guiceのバインディング

結束はGuiceに対するもので、配線はSpringに対するものです。バインディングでは、Guiceが依存関係をクラスに注入する方法を定義します。

バインディングは、

com.google.inject.AbstractModule

の実装で定義されています。

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Communicator.class).to(DefaultCommunicatorImpl.class);
    }
}

このモジュール実装は、

Communicator

変数が見つかった場合は常に

DefaultCommunicatorImpl

のインスタンスをインジェクトすることを指定します。

  • このメカニズムのもう一つの具体例は

    named binding

    ** です。次の変数宣言を考えてください。

@Inject @Named("DefaultCommunicator")
Communicator communicator;

このために、次のようなバインディング定義があります。

@Override
protected void configure() {
    bind(Communicator.class)
      .annotatedWith(Names.named("DefaultCommunicator"))
      .to(Communicator.class);
}

このバインディングは、

@ Named(“ DefaultCommunicator”)

アノテーションが付けられた変数に

Communicator

のインスタンスを提供します。


@ Inject

アノテーションと

@ Named

アノテーションは、JavaEEのCDIからのローンアノテーションのように見えますが、それらはそうです。これらは

com.google.inject。**

パッケージに含まれています – IDEを使用するときは、正しいパッケージからインポートするように注意する必要があります。



ヒント:


Guiceが提供する

@ Inject



@ Named

を使用すると述べたのですが、Guiceは

javax.inject.Inject

と__javax.inject.Namedをサポートしています。アノテーション


  • constructor binding

    ** を使用して、デフォルトの引数なしのコンストラクタがない依存関係を注入することもできます。

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Boolean.class).toInstance(true);
        bind(Communication.class).toConstructor(
          Communication.class.getConstructor(Boolean.TYPE));
}

上記のスニペットは、

boolean

引数を取るコンストラクタを使用して

Communication

のインスタンスを挿入します。

Boolean

クラスの

untargeted binding

** を定義することによって、コンストラクターに

true

引数を提供します。

この

untargeted binding



boolean

パラメータを受け付けるバインディング内のコンストラクタに熱心に提供されます。このアプローチでは、Communicationのすべての依存関係が注入されます。

  • コンストラクタ固有のバインディングへのもう1つのアプローチは

    instance binding

    ** です。ここではバインディングにインスタンスを直接提供します。

public class BasicModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(Communication.class)
          .toInstance(new Communication(true));
    }
}


Communication

変数が宣言されている場合は常に、このバインディングは

Communication

クラスのインスタンスを提供します。

ただしこの場合、クラスの依存関係ツリーは自動的に配線されません。重い初期化や依存性注入が必要ない場合は、このモードの使用を制限してください。


4依存性注入の種類

Guiceは、DIパターンで予想されるような標準的な種類の注射をサポートしています。

Communicator

クラスでは、さまざまなタイプの

CommunicationMode

を注入する必要があります。


4.1. フィールドインジェクション

@Inject @Named("SMSComms")
CommunicationMode smsComms;

名前に基づいてターゲットインジェクションを実装するための修飾子としてオプションの

@ Named

アノテーションを使用する


4.2. メソッドインジェクション

ここでは、注入を達成するためにセッターメソッドを使用します。

@Inject
public void setEmailCommunicator(@Named("EmailComms") CommunicationMode emailComms) {
    this.emailComms = emailComms;
}


4.3. コンストラクタインジェクション

コンストラクタを使って依存関係を注入することもできます。

@Inject
public Communication(@Named("IMComms") CommunicationMode imComms) {
    this.imComms= imComms;
}


4.4. 暗黙のインジェクション

Guiceは、

Injector



java.util.Logger

のインスタンスなど、いくつかの汎用コンポーネントを暗黙的にインジェクトします。サンプル全体を通してロガーを使用していることに気付くでしょうが、実際のバインディングは見つからないでしょう。


5 Guiceでスコーピング

Guiceは、他のDIフレームワークでこれまで使用してきたスコープとスコープメカニズムをサポートしています。 Guiceはデフォルトで、定義された依存関係の新しいインスタンスを提供します。


5.1. シングルトン

アプリケーションにシングルトンを注入しましょう。

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class).in(Scopes.SINGLETON);


in(Scopes.SINGLETON)

は、

@ Named(“ AnotherCommunicator”)

を持つ

Communicator

フィールドにシングルトンが挿入されることを指定します。このシングルトンはデフォルトで遅延開始されます。


5.2. 熱心なシングルトン

さて、熱心なシングルトンを注入しましょう:

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class)
  .asEagerSingleton();


asEagerSingleton()

呼び出しは、シングルトンを熱心にインスタンス化されたものとして定義します。

これら2つのスコープに加えて、Guiceはカスタムスコープと、JavaEEによって提供されるWebのみの

@ RequestScoped

および

@ SessionScoped

アノテーションをサポートします(これらのアノテーションのGuice提供バージョンはありません)。


6. Guiceでのアスペクト指向プログラミング

Guiceは、AOPAllianceのアスペクト指向プログラミングの仕様に準拠しています。この例では、4つのステップでメッセージ送信の追跡に使用する典型的なロギングインターセプターを実装できます。


ステップ1 – AOPAllianceの




MethodInterceptor

を実装する


:

public class LoggingInterceptor implements MethodInterceptor {

    @Inject
    Logger logger;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object[]objectArray = invocation.getArguments();
        int i = 0;
        for (Object object : objectArray) {
            logger.info("Sending message: " + object.toString());
        }
        return invocation.proceed();
    }
}


ステップ2 – プレーンなJavaアノテーション

を定義します.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MessageSentLoggable {
}


ステップ3 –

Matcher

:

のバインディングを定義する


Matcher

は、AOPアノテーションが適用されるコンポーネントを指定するために使用するGuiceクラスです。この場合、注釈を__CommunicationModeの実装に適用する必要があります。

public class AOPModule extends AbstractModule {

    @Override
    protected void configure() {
        bindInterceptor(
            Matchers.any(),
            Matchers.annotatedWith(MessageSentLoggable.class),
            new MessageLogger()
        );
    }
}

ここでは、

MessageLogger

インターセプターを

any

クラスに適用する

Matcher

を指定しています。これには、そのメソッドに

MessageSentLoggable

アノテーションが適用されています。


ステップ4 – アノテーションを

CommunicationMode

に適用してモジュールをロードします

@Override
@MessageSentLoggable
public boolean sendMessage(String message) {
    logger.info("SMS message sent");
    return true;
}

public static void main(String[]args) {
    Injector injector = Guice.createInjector(new BasicModule(), new AOPModule());
    Communication comms = injector.getInstance(Communication.class);
}


7. 結論

Guiceの基本的な機能を見てきたところで、Guiceのインスピレーションが春から来たところを見ることができます。


JSR-330

のサポートとともに、Guiceはインジェクション重視のDIフレームワークを目指しています(Springは、プログラミングの利便性のためにエコシステム全体を提供するのに対して必ずしもDIだけではありません)、DIの柔軟性を求める開発者を対象としています。


Guiceはまた非常に拡張性があります

、プログラマがフレームワークの柔軟で創造的な使用をもたらすポータブルプラグインを書くことを可能にします。これは、Guiceがサーブレット、JSF、JPA、およびOSGiなどの最も一般的なフレームワークおよびプラットフォーム用にすでに提供している広範な統合に加えています。

このチュートリアルで使用されているすべてのソースコードは、https://github.com/eugenp/tutorials/tree/master/guice[GitHubプロジェクト]にあります。