PR

DI関連のその他の機能

 サンプル・アプリケーションの作成では使用しませんでしたが,他にも便利な機能がたくさんあります。ここでは,そのうちの四つを紹介します。アプリケーション作成時に,必要に応じて利用してください。

機能1:アノテーションでのインジェクションの指定(@Inject)

 サンプル・アプリケーションではsetterメソッドを使用しましたが,@Injectは以下のようないろいろな場所に付けてインジェクションを行うことができます。

  • すべてのメソッド(setterでなくてもよい)
  • コンストラクタ
  • フィールド
  •  リスト9リスト10リスト11はそれらの例です。

    
    package jp.co.nikkeibp.itpro.guice.hello;
    import com.google.inject.Inject;
    public class Client {
        private HelloService helloService = null;
        @Inject
        public void createService(HelloService helloService) {
        this.helloService = helloService;
        }
        public void execute() {
        helloService.sayHello();
        }
    }
    リスト9●インジェクションの対象クラス(setter以外のメソッド)

    
    package jp.co.nikkeibp.itpro.guice.hello;
    import com.google.inject.Inject;
    public class Client {
        private HelloService helloService = null;
        @Inject
        public Client(HelloService helloService) {
        this.helloService = helloService;
        }
        public void execute() {
        helloService.sayHello();
        }
    }
    リスト10●インジェクションの対象クラス(コンストラクタ)

    
    package jp.co.nikkeibp.itpro.guice.hello;
    import com.google.inject.Inject;
    public class Client {
        @Inject
        private HelloService helloService = null;
        public void execute() {
        helloService.sayHello();
        }
    }
    リスト11●インジェクションの対象クラス(フィールド)

     ただし,@Injectを付けたメソッドなどに,モジュールでインジェクションの設定を行わないとリスト12のように例外(com.google.inject.ConfigurationException)がスローされ,アプリケーションの起動に失敗します。

    
    Exception in thread "main" com.google.inject.ConfigurationException:
    Error at jp.co.nikkeibp.itpro.guice.hello.Client.helloService
    (Client.java:15)
    Binding to jp.co.nikkeibp.itpro.guice.hello.HelloService
    not found. No bindings to that type were found.
    リスト12●インジェクションを行わなかった場合に出力されるExceptionとそのメッセージ

     リスト13のフィールドのように,あらかじめ実装クラスがすでに決まっているがインジェクションで値を変える可能性がある場合は,@Injectの引数に「optional = true」を渡して設定します。

    
    // 実装クラスがすでに記述されているフィールド
    private HelloService helloService = new HelloServiceImpl();
    @Inject(optional = true)
    public void setHelloService(HelloService helloService) {
        this.helloService = helloService;
    }
    リスト13●「optional=true」の設定例

     こうすると,モジュールでインジェクションの設定を行わなくても例外はスローされません。ただし,これはコンストラクタには使用できない点に注意してください。

    機能2:アノテーションでの依存性の指定(@ImplementedBy)

     サンプル・アプリケーションでは,モジュールを使用して依存性の解決を行いました。もう一つ,インタフェースで「@ImplementedBy」を使用し,その引数に実装クラスを指定することで依存性を解決することも可能です。インタフェースと実装クラスを分ける場合,結果的に一対一になってしまうことも多いので,そういった場合に使用するとインジェクションの設定が簡単になります。

     サンプル・アプリケーションを書き換えて,@ImplementedByを使用するとリスト14のようになります。

    
    package jp.co.nikkeibp.itpro.guice.hello;
    import com.google.inject.ImplementedBy;
    @ImplementedBy(HelloServiceImpl.class)
    public interface HelloService {
        public void sayHello();
    }
    リスト14●サービスのインタフェース(@ImplementedBy使用)

     この場合他に設定することがないため,モジュールは必要ありません。Module.javaは作成せず,メインメソッドでリスト15のように,Guice#createInjectorメソッドを引数なしに書き換えることが可能です。

    
    package jp.co.nikkeibp.itpro.guice.hello;
    import com.google.inject.Guice;
    import com.google.inject.Injector;
    public class Main {
        public static void main(String[] args) {
        Injector injector = Guice.createInjector();
        Client client = injector.getInstance(Client.class);
        client.execute();
        }
    }
    リスト15●メインクラス(モジュールを使用しない)

    機能3:名前を指定したインジェクション(@Named)

     インジェクションの対象に名前を付けることが可能です。名前を付けるにはリスト16のように@Injectとともに「@Named」を使用します。同じクラスのフィールドが二つある場合に,それを区別するために使用します。

    
    package jp.co.nikkeibp.itpro.guice.hello;
    import com.google.inject.Inject;
    import com.google.inject.name.Named;
    public class Client {
        @Inject
        @Named("hello")
        private HelloService helloService = null;
        public void execute() {
        helloService.sayHello();
        }
    }
    リスト16●インジェクションの対象クラス(フィールド)

     @Namedを使用して名前を付けると,モジュールでは,annotatedWithメソッドとNames#namedメソッドを使用して,その名前を指定してインジェクションを行うことが可能になります。

    
    package jp.co.nikkeibp.itpro.guice.hello;
    import com.google.inject.AbstractModule;
    import com.google.inject.name.Names;
    public class Module extends AbstractModule {
        @Override
        protected void configure() {
        bind(HelloService.class).annotatedWith(Names.named("hello")).to(
            HelloServiceImpl.class);
        }
    }
    リスト17●インジェクションの名前を指定したモジュールクラス

    機能4:プロバイダ(Provider)の利用

     モジュールで,ただオブジェクトを指定するだけでなく,プロバイダを使用することによって処理を行うことができます。プロバイダは,com.google.inject.Providerインタフェースを実装して作成します。その際ジェネリクスでgetメソッドの戻り値を指定し,その値がインジェクションされます。

    
    package jp.co.nikkeibp.itpro.guice.hello;
    import com.google.inject.Provider;
    public class HelloProvider implements Provider {
        public HelloService get() {
        return new HelloServiceImpl();
        }
    }
    リスト18●プロバイダの利用例

     モジュールでは,toProviderメソッドを使用して,作成したプロバイダを指定します。

    
    package jp.co.nikkeibp.itpro.guice.hello;
    import com.google.inject.AbstractModule;
    public class Module extends AbstractModule {
        @Override
        protected void configure() {
        bind(HelloService.class).toProvider(HelloProvider.class);
        }
    }
    リスト19●モジュール内でのプロバイダの指定

     実行すればgetメソッドが呼ばれ,HelloServiceImplのオブジェクトがインジェクションされます。この場合は,モジュールを使う方法に比べてクラスが増えているだけですが,getメソッド内にロジックを書けば,インジェクションするごとに処理を行うことができ,値が異なるオブジェクトをインジェクションすることができます。