Google Guiceガイド

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プロジェクト]にあります。