第23回 Guiceその1
今回のテーマは「Guice」です。
まず「Guiceとは何ぞや」というところから始めます。 Guice(ジュースと読みます)はGoogleがオープンソースとして公開したDIフレームワークです。 SpringやSeasar2といった有名どころとの大きな違いは、依存性注入の設定をxmlファイルに記述するのではなく、Javaで記述するという点です。
Javaで依存性注入の設定をするということに、どういうメリットがあるのでしょうか。 実はxmlによる設定の場合、クラス名などの記述ミスがあっても、プログラムを実行してみないと間違いがわからないのです。 Javaでの設定の場合は、コンパイル時点でエラーが発生するので、実行前に記述ミスが明らかになります。 また、JUnitなどのテスティングフレームワークによる単体テストも容易になります。
それでは、簡単なプログラムを使って、GuiceによるDIを説明していきます。
public class Player { public void play() { System.out.println("ウォームアップします。"); Badminton badminton = new Badminton(); badminton.play(); System.out.println("クールダウンします。"); } }
public class Badminton { public void play() { System.out.println("バドミントンして遊びます。"); } }
BadmintonクラスがないとPlayerクラスはコンパイルさえできません。 依存関係の解決前の状態です。
では、Guiceを使用してコードを変更していきます。 まずSportsインターフェースを作成し、Badmintonクラスを変更します。
public interface Sports { void play(); }
public class Badminton implements Sports { @Override public void play() { System.out.println("バドミントンして遊びます。"); } }
次にSportsインターフェースとBadmintonクラスを結びつけるためのSportsModuleクラスを作成します。
GuiceではModuleインターフェースを実装することで依存性の注入の設定を行うのですが、直接Moduleインターフェースを実装するのではなく、AbstractModuleを継承することで、若干記述を減らすことができます。
configureメソッドでSportsインターフェースとBadmintonクラスを結びつけるための具体的な処理を行っています。
なお、bind(クラスA).to(クラスB)の場合において、クラスAとクラスBがis-a関係にないとコンパイルエラーが発生します。
これはジェネリクスの型変数によって実現されている処理です。
public class SportsModule extends AbstractModule { @Override protected void configure() { bind(Sports.class).to(Badminton.class); } }
PlayerクラスにはSports型のフィールドを持たせて、Injectアノテーションを記述します。
これにより、Sports型のフィールドに依存性が注入されます。
Injectアノテーションはフィールドに限らず、コンストラクタや任意のメソッドに指定できます。
また、アクセス修飾子がprivateであっても関係ありません。
import com.google.inject.Inject; public class Player { @Inject private Sports sports; public void play() { System.out.println("ウォームアップします。"); sports.play(); System.out.println("クールダウンします。"); } }
最後にPlayerクラスを実行するクラスを作成します。
ここで先ほどのSportsModuleクラスを使用して、注入を実行するInjectorを作成します。
作成されたInjectorによって、Playerクラスのインスタンスが依存性注入された状態で生成されます。
import com.google.inject.Guice; import com.google.inject.Injector; public class Main { public static void main(String[] args) { Injector injector = Guice.createInjector(new SportsModule()); Player player = injector.getInstance(Player.class); player.play(); } }
以上の構成により、PlayerクラスとBadmintonクラスの依存関係が解消されました。
ちなみに、new演算子でPlayerクラスのインスタンスを生成した場合、playメソッドを実行したら確実にNullPointerExceptionが発生します。 プログラマにnew演算子を使わせないためには、コンストラクタをprivateにしましょう。
Guiceはコンストラクタがprivateなクラスでもインスタンス生成できますので、PlayerクラスとBadmintonクラスのコンストラクタを
privateにした場合でも、上記のコードは問題なく動作します。
次週も引き続きGuiceによるDIを学びます。