1. 序章

このチュートリアルでは、高速で軽量な依存性注入フレームワークであるDagger2を見ていきます。

このフレームワークはJavaとAndroidの両方で使用できますが、コンパイル時のインジェクションから得られる高性能により、後者の主要なソリューションになります。

2. 依存性注入

ちょっとした注意として、依存性注入は、プログラムのフローがプログラム自体によって制御される、より一般的な制御の反転の原則の具体的なアプリケーションです。

これは、他のオブジェクトに必要なオブジェクト(または依存関係)のインスタンスを提供する外部コンポーネントを介して実装されます。

また、フレームワークが異なれば、依存性注入をさまざまな方法で実装します。 特に、これらの違いの中で最も注目すべきものの1つは、インジェクションが実行時に行われるかコンパイル時に行われるかです。

実行時DIは通常、リフレクションに基づいています。リフレクションは使用が簡単ですが、実行時は遅くなります。 ランタイムDIフレームワークの例はSpringです。

一方、コンパイル時のDIは、コード生成に基づいています。 これは、すべての重い操作がコンパイル中に実行されることを意味します。 コンパイル時のDIは複雑さを増しますが、一般的にはより高速に実行されます。

ダガー2はこのカテゴリーに分類されます。

3. Maven/Gradle構成

プロジェクトでDaggerを使用するには、dagger依存関係pom.xmlに追加する必要があります。

<dependency>
    <groupId>com.google.dagger</groupId>
    <artifactId>dagger</artifactId>
    <version>2.16</version>
</dependency>

さらに、注釈付きクラスをインジェクションに使用されるコードに変換するために使用されるDaggerコンパイラ含める必要があります。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.6.1</version>
    <configuration>
         <annotationProcessorPaths>
              <path>
                  <groupId>com.google.dagger</groupId>
                  <artifactId>dagger-compiler</artifactId>
                  <version>2.16</version>
              </path>
         </annotationProcessorPaths>
    </configuration>
</plugin>

この構成では、Mavenは生成されたコードを target /generated-sources /annotationsに出力します。

このため、コード補完機能のいずれかを使用する場合は、IDEをさらに構成する必要があります。 一部のIDEは注釈プロセッサを直接サポートしていますが、他のIDEではこのディレクトリをビルドパスに追加する必要がある場合があります。

または、AndroidとGradleを使用している場合は、両方の依存関係を含めることができます。

compile 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'

プロジェクトにDaggerが含まれているので、サンプルアプリケーションを作成して、それがどのように機能するかを確認しましょう。

4. 実装

この例では、コンポーネントを注入して車を作成しようとします。

現在、 Daggerは、多くの場所で標準のJSR-330アノテーションを使用しています。1つは@Injectです。

フィールドまたはコンストラクターに注釈を追加できます。 ただし、 Daggerはプライベートフィールドでのインジェクションをサポートしていないため、カプセル化を維持するためにコンストラクターインジェクションを使用します。

public class Car {

    private Engine engine;
    private Brand brand;

    @Inject
    public Car(Engine engine, Brand brand) {
        this.engine = engine;
        this.brand = brand;
    }

    // getters and setters

}

次に、インジェクションを実行するためのコードを実装します。 具体的には、以下を作成します。

  • module は、オブジェクトの依存関係を提供または構築するクラスであり、
  • インジェクターを生成するために使用されるインターフェースであるコンポーネント

複雑なプロジェクトには複数のモジュールとコンポーネントが含まれる場合がありますが、非常に基本的なプログラムを扱っているため、それぞれ1つで十分です。

それらを実装する方法を見てみましょう。

4.1. モジュール

モジュールを作成するには、クラスに@Moduleアノテーションを付ける必要があります。 このアノテーションは、クラスが依存関係をコンテナーで使用できるようにすることができることを示しています。

@Module
public class VehiclesModule {
}

次に、依存関係を構築するメソッドに@Providesアノテーションを追加する必要があります

@Module
public class VehiclesModule {
    @Provides
    public Engine provideEngine() {
        return new Engine();
    }

    @Provides
    @Singleton
    public Brand provideBrand() { 
        return new Brand("Baeldung"); 
    }
}

また、特定の依存関係のスコープを構成できることに注意してください。 この場合、 Brand インスタンスにシングルトンスコープを指定して、すべての車のインスタンスが同じブランドオブジェクトを共有するようにします。

4.2. 成分

次に、コンポーネントインターフェイスを作成します 。 これは、Carインスタンスを生成し、によって提供される依存関係を注入するクラスです。 VehiclesModule

簡単に言うと、 Car を返すメソッドシグネチャが必要であり、クラスを@Componentアノテーションでマークする必要があります。

@Singleton
@Component(modules = VehiclesModule.class)
public interface VehiclesComponent {
    Car buildCar();
}

モジュールクラスを引数として@Componentアノテーションに渡したことに注目してください。 そうしなかった場合、ダガーは車の依存関係を構築する方法を知りません。

また、モジュールはシングルトンオブジェクトを提供するため、 Daggerではスコープ外のコンポーネントがスコープ付きバインディングを参照できないため、コンポーネントに同じスコープを指定する必要があります。

4.3. クライアントコード

最後に、 mvn compile を実行して、アノテーションプロセッサをトリガーし、インジェクターコードを生成できます。

その後、インターフェースと同じ名前で、接頭辞「Dagger」が付いたコンポーネント実装が見つかります。

@Test
public void givenGeneratedComponent_whenBuildingCar_thenDependenciesInjected() {
    VehiclesComponent component = DaggerVehiclesComponent.create();

    Car carOne = component.buildCar();
    Car carTwo = component.buildCar();

    Assert.assertNotNull(carOne);
    Assert.assertNotNull(carTwo);
    Assert.assertNotNull(carOne.getEngine());
    Assert.assertNotNull(carTwo.getEngine());
    Assert.assertNotNull(carOne.getBrand());
    Assert.assertNotNull(carTwo.getBrand());
    Assert.assertNotEquals(carOne.getEngine(), carTwo.getEngine());
    Assert.assertEquals(carOne.getBrand(), carTwo.getBrand());
}

5. 春のアナロジー

Springに精通している人は、2つのフレームワークの間にいくつかの類似点があることに気付いたかもしれません。

Daggerの@Moduleアノテーションは、Springのステレオタイプアノテーション(たとえば、 @Service @Controller)と非常によく似た方法で、コンテナーにクラスを認識させます。 …)。 同様に、@Provides@Componentは、それぞれSpringの @Bean@Lookupとほぼ同等です。

Springには@Scopeアノテーションもあり、 @Singleton と相関していますが、Springはデフォルトでシングルトンスコープを想定しているのに対し、DaggerはデフォルトでSpring開発者が参照する可能性があることに注意してください。プロトタイプスコープとして、依存関係が必要になるたびにプロバイダーメソッドを呼び出します。

6. 結論

この記事では、基本的な例を使用してDagger2をセットアップして使用する方法について説明しました。 また、実行時とコンパイル時のインジェクションの違いについても検討しました。

いつものように、記事のすべてのコードはGitHub利用できます。