1. 概要

以前の記事では、Ratpackを使用してスケーラブルなアプリケーションを構築する方法を示しました。

このチュートリアルでは、 GoogleGuiceRatpackを依存関係管理エンジンとして使用する方法についてさらに説明します。

2. なぜGoogleGuiceなのか?

Google Guice は、 ApacheLicenseの下でGoogleによってリリースされたJavaプラットフォーム用のオープンソースソフトウェアフレームワークです。

構成が簡単な非常に軽量な依存関係管理モジュールです。 さらに、使いやすさのために、コンストラクターレベルの依存性注入のみを許可します。

Guice の詳細については、こちらをご覧ください。

3. RatpackでGuiceを使用する

3.1. Mavenの依存関係

Ratpackは、Guice依存関係をファーストクラスでサポートしています。 したがって、Guiceの外部依存関係を手動で追加する必要はありません。 それはすでにRatpackで事前に構築されています。 RatpackGuiceサポートの詳細については、こちらをご覧ください。

したがって、pom.xmlに次のコアRatpack依存関係を追加する必要があります。

<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-core</artifactId>
    <version>1.4.5</version>
</dependency>

最新バージョンは、 Maven Centralで確認できます。

3.2. サービスモジュールの構築

Maven 構成が完了したら、サービスを構築し、ここでの例でいくつかの単純な依存性注入をうまく利用します。

1つのサービスインターフェイスと1つのサービスクラスを作成しましょう。

public interface DataPumpService {
    String generate();
}

これは、インジェクターとして機能するサービスインターフェイスです。 次に、それを実装し、サービスメソッド generate():を定義するサービスクラスを構築する必要があります。

public class DataPumpServiceImpl implements DataPumpService {

    @Override
    public String generate() {
        return UUID.randomUUID().toString();
    }

}

ここで注意すべき重要な点は、RatpackのGuiceモジュールを使用しているため、Guiceの@ImplementedByまたは@Injectアノテーションを使用してサービスクラスを手動で注入する必要がないことです。

3.3. 依存関係の管理

GoogleGuiceで依存関係管理を実行する方法は2つあります。

1つ目はGuiceAbstractModuleを使用する方法で、もう1つはGuiceのインスタンスバインディングメカニズムメソッドを使用する方法です。

public class DependencyModule extends AbstractModule {

    @Override
    public void configure() {
        bind(DataPumpService.class).to(DataPumpServiceImpl.class)
          .in(Scopes.SINGLETON);
    }

}

ここで注意すべきいくつかのポイント:

  • AbstractModule を拡張することにより、デフォルトの configure()メソッドをオーバーライドします
  • DataPumpServiceImplクラスを以前に構築されたサービスレイヤーであるDataPumpServiceインターフェイスにマッピングしています
  • また、シングルトン方式として依存関係を注入しました。

3.4. 既存のアプリケーションとの統合

依存関係管理の構成の準備ができたので、それを統合しましょう。

public class Application {

    public static void main(String[] args) throws Exception {

      RatpackServer
          .start(server -> server.registry(Guice
            .registry(bindings -> bindings.module(DependencyModule.class)))
            .handlers(chain -> chain.get("randomString", ctx -> {
                DataPumpService dataPumpService = ctx.get(DataPumpService.class);
                ctx.render(dataPumpService.generate().length());
            })));
    }
}

ここでは、 repository()を使用して、AbstractModuleを拡張するDependencyModuleクラスをバインドしました。 RatpackのGuiceモジュールは、必要な残りの部分を内部的に実行し、アプリケーションContextにサービスを注入します。

application-context、で利用できるため、アプリケーションのどこからでもサービスインスタンスをフェッチできるようになりました。 ここでは、現在のコンテキストから DataPumpService インスタンスをフェッチし、 / randomString URLをサービスのgenerate()メソッドでマッピングしました。

その結果、 / randomString URLがヒットするたびに、ランダムな文字列フラグメントが返されます。

3.5. ランタイムインスタンスバインディング

前述のように、Guiceのインスタンスバインディングメカニズムを使用して、実行時に依存関係の管理を行います。 AbstractModuleの代わりにGuiceのbindInstance()メソッドを使用して依存関係を注入することを除けば、以前の手法とほぼ同じです。

public class Application {

    public static void main(String[] args) throws Exception {

      RatpackServer.start(server -> server
        .registry(Guice.registry(bindings -> bindings
        .bindInstance(DataPumpService.class, new DataPumpServiceImpl())))
        .handlers(chain -> chain.get("randomString", ctx -> {
            DataPumpService dataPumpService = ctx.get(DataPumpService.class);
            ctx.render(dataPumpService.generate());
        })));
    }
}

ここでは、 bindInstance()を使用して、インスタンスのバインドを実行しています。 DataPumpServiceインターフェイスをDataPumpServiceImplクラスに挿入します。

このようにして、前の例で行ったように、サービスインスタンスをapplication-contextに挿入できます。

依存関係の管理には2つの手法のいずれかを使用できますが、依存関係の管理モジュールをアプリケーションコードから完全に分離するため、AbstractModuleを使用することをお勧めします。 このようにして、コードは非常にクリーンになり、将来的に保守しやすくなります。

3.6. ファクトリーバインディング

ファクトリバインディングと呼ばれる依存関係管理のもう1つの方法もあります。 Guiceのの実装とは直接関係ありませんが、Guiceと並行して機能することもできます。

ファクトリクラスは、クライアントを実装から切り離します。 単純なファクトリは、静的メソッドを使用して、インターフェイスのモック実装を取得および設定します。

作成済みのサービスクラスを使用して、ファクトリバインディングを有効にすることができます。 DependencyModule GuiceのAbstractModuleクラスを拡張する)のように1つのファクトリクラスを作成し、静的メソッドを介してインスタンスをバインドする必要があります。

public class ServiceFactory {

    private static DataPumpService instance;

    public static void setInstance(DataPumpService dataPumpService) {
        instance = dataPumpService;
    }

    public static DataPumpService getInstance() {
        if (instance == null) {
            return new DataPumpServiceImpl();
        }
        return instance;
    }
}

ここでは、ファクトリクラスにサービスインターフェイスを静的に挿入しています。 したがって、一度にそのインターフェイスの1つのインスタンスのみがこのファクトリクラスで使用可能になります。 次に、サービスインスタンスを設定およびフェッチするための通常の getter /setterメソッドを作成しました。

ここで注意すべき点は、 getter メソッドでは、サービスのインスタンスが1つだけ存在するかどうかを確認するために、1つの明示的なチェックを行ったことです。 nullの場合は、実装サービスクラスのインスタンスを作成し、同じものを返しただけです。

その後、アプリケーションチェーンでこのファクトリインスタンスを使用できます。

.get("factory", ctx -> ctx.render(ServiceFactory.getInstance().generate()))

4. テスト

RatpackMainClassApplicationUnderTestを使用して、Ratpackの内部JUnitテストフレームワークを使用してアプリケーションをテストします。 必要な依存関係( ratpack-test )を追加する必要があります。

ここで注意すべき点は、URLコンテンツは動的であるため、テストケースの作成中に予測することはできないということです。 したがって、テストケースでは / randomStringURLエンドポイントのコンテンツの長さに一致します。

@RunWith(JUnit4.class)
public class ApplicationTest {

    MainClassApplicationUnderTest appUnderTest
      = new MainClassApplicationUnderTest(Application.class);

    @Test
    public void givenStaticUrl_getDynamicText() {
        assertEquals(21, appUnderTest.getHttpClient()
          .getText("/randomString").length());
    }
	
    @After
    public void shutdown() {
        appUnderTest.close();
    }
}

5. 結論

このクイック記事では、 GoogleGuiceRatpackで使用する方法を説明しました。

いつものように、完全なソースコードはGitHubから入手できます。