インジェクション
依存性注入パターンはふるまいと依存性解決を分離する。 ファクトリによって、依存を直接的にルックアップするより、依存を引き渡すことが推奨される。 依存をオブジェクトにセットすることはインジェクションと呼ぶ
コンストラクタインジェクション
コンストラクタインジェクションはインスタンス作成とインジェクションを組み合わせたものである。 コンストラクタに@Injectアノテーションを用いることにより使う。 このコンストラクタはパラメータとして依存性を受け取る。 大抵のコンストラクタでは、これらのパラメータをfinalフィールドに代入する。
public class RealBillingService implements BillingService { private final CreditCardProcessor processorProvider; private final TransactionLog transactionLogProvider; @Inject public RealBillingService(CreditCardProcessor processorProvider, TransactionLog transactionLogProvider) { this.processorProvider = processorProvider; this.transactionLogProvider = transactionLogProvider; }
もし、@Injectアノテーション付のコンストラクタが無い場合、(もしあれば)Guiceはpublicな引数無しのコンストラクタを使用する。 しかし、この場合にもアノテーションすることが推奨される。依存性注入されることのドキュメントになるからである。
コンストラクタインジェクションはユニットテストに最適である。 もし、そのクラスのすべての依存が一つのコンストラクタによって与えることができるならば、依存を設定し忘れることはない。 新たな依存を追加すると、呼び出しコードが不正になるため、そのコンパイルエラーを直しさえすれば、すべてがきちんとワイアリングされていることを確信することができる。
メソッドインジェクション
Guiceは@Injectアノテーション付のメソッドを使ってインジェクションする。 依存はパラメータで表され、インジェクタはそのメソッドの起動に先立って依存を解決する。 インジェクトされるメソッドは、いくつのパラメータを持ってもよいし、メソッド名はどのようなものでもよい。
public class PayPalCreditCardProcessor implements CreditCardProcessor { private static final String DEFAULT_API_KEY = "development-use-only"; private String apiKey = DEFAULT_API_KEY; @Inject public void setApiKey(@Named("PayPal API key") String apiKey) { this.apiKey = apiKey; }
フィールドインジェクション
Guiceは@Injectアノテーション付のフィールドにインジェクトする。 これが最も簡単なインジェクト方法であるが、しかし最もテストしにくい。
public class DatabaseTransactionLogProvider implements Provider<TransactionLog> { @Inject Connection connection; public TransactionLog get() { return new DatabaseTransactionLog(connection); } }
finalフィールドにインジェクションするのは避けるべきである。それはweak semanticsである。
オプショナルインジェクション
依存が存在するときにだけインジェクションを行い、存在しない場合はデフォルトにフォールバックするのが便利なこともある。 メソッドとフィールドのインジェクションはオプションにすることもできる。 その場合、もし依存が存在しなければGuiceは何のエラーも発生せずに蒸しする。 オプショナルインジェクションを用いるには、@Inject(optional=true)アノテーションを使う。
public class PayPalCreditCardProcessor implements CreditCardProcessor { private static final String SANDBOX_API_KEY = "development-use-only"; private String apiKey = SANDBOX_API_KEY; @Inject(optional=true) public void setApiKey(@Named("PayPal API key") String apiKey) { this.apiKey = apiKey; }
オプショナルインジェクションとその場バインディングを混在させると、意外な結果が発生する場合がある。 例えば、次のフィールドは、Dateの明示的バインディングがなくても常にインジェクトされる。 なぜなら、Dateはその場バインディングで利用可能なpublicな引数無しのコンストラクタを持つからである。
@Inject(optional=true) Date launchDate;
リクエストインジェクション
メソッド及びフィールドインジェクションは既存のインスタンスを初期化するのに用いることができる。 これはInjector.injectMembersによって行われる。
public static void main(String[] args) { Injector injector = Guice.createInjector(...); CreditCardProcessor creditCardProcessor = new PayPalCreditCardProcessor(); injector.injectMembers(creditCardProcessor);
スタティックインジェクション
When migrating an application from static factories to Guice, it is possible to change incrementally. Static injection is a helpful crutch here. It makes it possible for objects to partially participate in dependency injection, by gaining access to injected types without being injected themselves. Use requestStaticInjection() in a module to specify classes to be injected at injector-creation time:
@Override public void configure() { requestStaticInjection(ProcessorFactory.class); ... }
Guice will inject class's static members that have the @Injected annotation:
class ProcessorFactory { @Inject static Provider<Processor> processorProvider; /** * @deprecated prefer to inject your processor instead. */ @Deprecated public static Processor getInstance() { return processorProvider.get(); } }
Static members will not be injected at instance-injection time. This API is not recommended for general use because it suffers many of the same problems as static factories: it's clumsy to test, it makes dependencies opaque, and it relies on global state.
自動インジェクション
Guice以下のすべてを自動的にインジェクトする。
- bind()ステートメント中のtoInstance()に指定されたインスタンス
- bind()ステートメント中のtoProvider()に指定されたプロバイダインスタンス
The objects will be injected while the injector itself is being created. If they're needed to satisfy other startup injections, Guice will inject them before they're used.