コンパイル時依存性注入(Scala) / Play2.6【翻訳】

https://playframework.com/documentation/2.6.x/ScalaCompileTimeDependencyInjection を google 翻訳した。

---
Play framework/Scala開発者のためのプレイ/主なコンセプト/コンパイル時依存性注入

# コンパイル時依存性注入

デフォルトでは、Playはランタイム依存性注入のメカニズムを提供します。つまり、ランタイムまで依存性が配線されていない依存性注入です。このアプローチには長所と短所の両方があり、主な利点は定型コードの最小化です。主な欠点は、アプリケーションの構築がコンパイル時に検証されないことです。

Scala開発で普及している別のアプローチは、コンパイル時依存性注入を使用することです。最も単純なコンパイル時DIは、依存関係を手作業で構築し配線することで実現できます。マクロベースのオートワイヤリングツール、暗黙的な自動配線技術、さまざまな形のケーキパターンなど、他の高度な技術やツールも存在します。これらはすべてコンストラクタや手動配線の上に簡単に実装できるため、PublicコンストラクタとファクトリメソッドをAPIとして提供することで、Playのコンパイル時依存性注入のサポートが提供されます。

パブリックコンストラクタとファクトリメソッドを提供することに加えて、Playモジュールのすべてのアウトオブボックスモジュールは、便宜上、ケーキパターンの軽量フォームを実装するいくつかの trait を提供します。これらはパブリックコンストラクタの上に構築され、完全にオプションです。一部のアプリケーションでは、使用するのが適切ではありませんが、多くのアプリケーションでは、Playによって提供されるコンポーネントを配線するための非常に便利なメカニズムになります。これらの trait は、名前が Components で終了する命名規則に従います。たとえば、デフォルトのHikariCPベースのDB APIの実装では、 [HikariCPComponents](https://playframework.com/documentation/2.6.x/api/scala/play/api/db/HikariCPComponents.html) という trait が提供されます。

一般的にコンパイル時のDIやDIに慣れていない場合は、一般的なコンパイル時のDIと [Macwire](https://github.com/adamw/macwire) ライブラリで提供されるヘルパーのいくつかについて議論する [ScalaのDIに関するAdam Warskiのガイド](https://di-in-scala.github.io/) を読む価値があります。このアプローチは、Playによって提供される組み込みコンポーネントの trait と容易に統合できます。

以下の例では、組み込みコンポーネントヘルパーの trait を使用してPlayアプリケーションを手動で配線する方法を示します。提供されたコンポーネント trait のソースコードを読むことによって、これを他の依存性注入技術にも適応させるのは些細なことです。

## アプリケーションのエントリポイント

JVM上で動作するすべてのアプリケーションは、リフレクションによってロードされるエントリポイントを必要とします。アプリケーションが起動してもメインクラスはリフレクションによってロードされ、メインメソッドはリフレクションを使用して配置され呼び出されます。

Playの開発モードでは、Playで使用されるJVMとHTTPサーバーは、アプリケーションの再起動の間に実行されている必要があります。これを実装するために、Playは実装可能な [ApplicationLoader](https://playframework.com/documentation/2.6.x/api/scala/play/api/ApplicationLoader.html)  trait を提供します。アプリケーションローダーは、アプリケーションが再ロードされるたびに構築され、呼び出され、アプリケーションをロードします。

この trait の `load` メソッドは、アプリケーション自体が使い果たされ、アプリケーション自体では構築できないPlayアプリケーションが必要とするすべてのコンポーネントを含むアプリケーションローダー [Context](https://playframework.com/documentation/2.6.x/api/scala/play/api/ApplicationLoader$$Context.html) を引数として取ります。これらのコンポーネントは、devモードで機能を提供する目的で特に存在します。たとえば、ソースマッパーでは、Playエラーハンドラが例外がスローされた場所のソースコードをレンダリングすることができます。

この最も簡単な実装は、Play [BuiltInComComentsFromContext](https://playframework.com/documentation/2.6.x/api/scala/play/api/BuiltInComponentsFromContext.html) 抽象クラスを拡張することによって提供することができます。このクラスはコンテキストを取り、そのコンテキストに基づいてすべての組み込みコンポーネントを提供します。あなたが提供する必要があるのは、リクエストをルーティングするためのPlay用のルータだけです。以下は、ヌル・ルータを使用してこのように作成できる最もシンプルなアプリケーションです。

```scala
import play.api._
import play.api.ApplicationLoader.Context
import play.api.routing.Router
import play.filters.HttpFiltersComponents

class MyApplicationLoader extends ApplicationLoader {
  def load(context: Context) = {
    new MyComponents(context).application
  }
}

class MyComponents(context: Context)
  extends BuiltInComponentsFromContext(context)
  with HttpFiltersComponents {
  lazy val router = Router.empty
}
```

このアプリケーションローダーを使用するようにPlayを設定するには、 `play.application.loader` プロパティーを設定して、 `application.conf` ファイル内の完全修飾クラス名を指すようにします。

```
play.application.loader=MyApplicationLoader
```

さらに、組み込みのGuiceモジュールを使用する既存のプロジェクトを変更する場合は、 `build.sbt` 内の `libraryDependencies` から `guice` を削除することができます。

## ロギングの設定

Playのログを正しく設定するには、アプリケーションが返される前に `LoggerConfigurator` を実行する必要があります。 デフォルトの [BuiltInComponentsFromContext](https://playframework.com/documentation/2.6.x/api/scala/play/api/BuiltInComponentsFromContext.html) は `LoggerConfigurator` を呼び出しません。

この初期化コードは、アプリケーションローダーに追加する必要があります。

```scala
class MyApplicationLoaderWithInitialization extends ApplicationLoader {
  def load(context: Context) = {
    LoggerConfigurator(context.environment.classLoader).foreach {
      _.configure(context.environment, context.initialConfiguration, Map.empty)
    }
    new MyComponents(context).application
  }
}
```

Play 2.4.xから移行する場合、 `LoggerConfigurator` は `Logger.configure()` を置き換えるもので、異なるロギングフレームワークをカスタマイズすることができます。

## ルータを提供する

デフォルトでは、Playは [注入されたルートジェネレータ](https://playframework.com/documentation/2.6.x/ScalaDependencyInjection#Injected-routes-generator) を使用します。これにより、ルートファイル内の各コントローラと含まれているルータをルートファイルに表示されている順序で受け入れるコンストラクタを持つルータが生成されます。ルータのコンストラクタは、最初の引数として、 [HttpErrorHandler](https://playframework.com/documentation/2.6.x/api/scala/play/api/http/HttpErrorHandler.html) （パラメータバインディングエラーを処理するために使用されます）と接頭辞Stringを最後の引数として受け入れます。これをデフォルトで `"/"` にするオーバーロードされたコンストラクタも提供されます。

次のルート：

```
GET        /                    controllers.Application.index
GET        /foo                 controllers.Application.foo
->         /bar                 bar.Routes
GET        /assets/*file        controllers.Assets.at(path = "/public", file)
```

次のコンストラクタシグネチャを持つルータを生成します。

```
class Routes(
  override val errorHandler: play.api.http.HttpErrorHandler,
  Application_0: controllers.Application,
  bar_Routes_0: bar.Routes,
  Assets_1: controllers.Assets,
  val prefix: String
) extends GeneratedRouter {

  def this(
    errorHandler: play.api.http.HttpErrorHandler,
    Application_0: controllers.Application,
    bar_Routes_0: bar.Routes,
    Assets_1: controllers.Assets
  ) = this(Application_0, bar_Routes_0, Assets_1, "/")
  ...
}
```

パラメータの名前は意図的に明確に定義されていないことに注意してください（実際には、ハッシュマップの順序によっては、追加されるインデックスはランダムです）ので、これらのパラメータの名前には依存しないでください。

実際のアプリケーションでこのルータを使用するには：

```
import play.api._
import play.api.ApplicationLoader.Context
import play.filters.HttpFiltersComponents
import router.Routes

class MyApplicationLoader extends ApplicationLoader {
  def load(context: Context) = {
    new MyComponents(context).application
  }
}

class MyComponents(context: Context)
  extends BuiltInComponentsFromContext(context)
    with HttpFiltersComponents
    with controllers.AssetsComponents {
  lazy val barRoutes = new bar.Routes(httpErrorHandler)
  lazy val applicationController = new controllers.Application(controllerComponents)

  lazy val router = new Routes(httpErrorHandler, applicationController, barRoutes, assets)
}
```

## 他のコンポーネントの使用

前に説明したように、Playは他のコンポーネントで配線するためのさまざまなヘルパー trait を提供します。たとえば、メッセージモジュールを使いたい場合、 [I18nComponents](https://playframework.com/documentation/2.6.x/api/scala/play/api/i18n/I18nComponents.html) をコンポーネントのケーキに混ぜることができます：

```scala
import play.api.i18n._

class MyComponents(context: Context)
  extends BuiltInComponentsFromContext(context)
    with I18nComponents
    with HttpFiltersComponents {
  lazy val router = Router.empty

  lazy val myComponent = new MyComponent(messagesApi)
}

class MyComponent(messages: MessagesApi) {
  // ...
}
```

他のヘルパー trait は、 [CSRFComponents](https://playframework.com/documentation/2.6.x/api/scala/play/filters/csrf/CSRFComponents.html) または [AhCWSComponents](https://playframework.com/documentation/2.6.x/api/scala/play/api/libs/ws/ahc/AhcWSComponents.html) としても利用可能です