Ratpack Google Guiceの統合

Ratpack Google Guiceの統合

1. 概要

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

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

2. Google Guiceを選ぶ理由

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

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

Guiceの詳細については、hereを参照してください。

3. RatpackでGuiceを使用する

3.1. メーベン依存

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

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


    io.ratpack
    ratpack-core
    1.4.5

MavenCentralで最新バージョンを確認できます。

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’s Guiceモジュールを使用しているため、we don’t need to use Guice‘s @ImplementedBy or @Inject annotation to manually inject the service class.

3.3. 依存関係管理

Google Guiceを使用して依存関係管理を実行する方法は2つあります。

1つ目はGuiceAbstractModuleを使用することで、もう1つはGuiceのinstance bindingメカニズムメソッドを使用することです。

public class DependencyModule extends AbstractModule {

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

}

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

  • AbstractModuleを拡張することにより、デフォルトのconfigure()メソッドをオーバーライドします

  • 以前に構築されたサービスレイヤーであるDataPumpServiceインターフェイスを使用してDataPumpServiceImplクラスをマッピングしています

  • また、依存関係をSingletonの方法で挿入しました。

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());
            })));
    }
}

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

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

その結果、/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. ファクトリーバインディング

factory bindingと呼ばれる依存関係管理のもう1つの方法もあります。 Guice’sの実装とは直接関係ありませんが、これはGuiceと並行して機能することもできます。

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

すでに作成されているサービスクラスを使用して、ファクトリバインディングを有効にすることができます。 DependencyModuleGuice’s 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;
    }
}

Here, we’re statically injecting the service interface in the factory class。 したがって、一度にそのインターフェースの1つのインスタンスのみがこのファクトリクラスで利用可能になります。 次に、サービスインスタンスを設定およびフェッチするための通常のgetter/setterメソッドを作成しました。

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

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

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

4. テスト

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

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

@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. 結論

この簡単な記事では、Google GuiceRatpackとともに使用する方法を示しました。

いつものように、完全なソースコードはover on GitHubで利用できます。