google guiceで定数をインジェクション

はじめに


前へ | 目次 | 次へ

文字列のインジェクション

例えば、Springの場合、以下のデータソースの定義のように

<bean id="dataSource" 
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
    <property name="url" value="jdbc:hsqldb:hsql://localhost:9001" />
    <property name="username" value="sa" />
    <property name="password" value="" />
</bean>

依存するクラスのインスタンスでは無く何かしらの定数をインジェクションしたくなることはよくあると思います。上記の例のような環境依存的なものは、プログラム中にハードコーディングするのでは無くBean定義ファイルやプロパティファイル等に外出しするのが定石では無いでしょうか。

google guiceではそのような場合、以下のようにして定数をインジェクションすることが可能なようです。

  • 定数をインジェクションされるクラスのインターフェース
public interface ConstantInjectedService {
    public void execute();
}
  • 定数をインジェクションされるクラスの実装
public class ConstantInjectedServiceImpl implements ConstantInjectedService {
    @Inject
    @Named("name")
    private String name_;
    
    public void execute() {
        System.out.println("Injected name : " + name_);
    }
}
  • モジュールクラス
public class ConstantInjectorMocule extends AbstractModule {
    @Override
    protected void configure() {
        bindConstant().annotatedWith(Names.named("name")).to("Hoge");
        bind(ConstantInjectedService.class).to(ConstantInjectedServiceImpl.class).in(Scopes.SINGLETON);
    }
}
  • テスト用クラス
public class ConstantInjectTest {
    public static void main(String[] args) {
        Module module = new ConstantInjectorMocule();
        Injector injector = Guice.createInjector(module);
        ConstantInjectedService service = injector.getInstance(ConstantInjectedService.class);
        service.execute();
    }
}
  • 実行結果
Injected name : Hoge

上記は、例としてguiceが提供する「@Named」アノテーションを利用していますが、別にアノテーション自体はなんでもよくて、bindConstant().annotatedWith()メソッドとアノテーションを組み合わせてインジェクションするところがミソです。

一応、上記のようにアノテーションとbindConstant().annotatedWith()メソッドを利用して定数を(文字列じゃなくても)インジェクションすることは可能のようですが、正直に言うと個人的にはちょっと微妙です。

と、いうのも、結局のところモジュールクラスに定数をハードコーディングするのならばいわゆるコンスタントクラス的なものと大差無いような気がするし、上記の手法では「環境依存的な設定を外部ファイルに外出しする」といったことを仮にやりたくなったとしてもできません。やりたくならなけれべ別に構いませんけど、全ての人が絶対にやりたくならないとも言い切れませんので、この辺に関しては別途「設定ファイルとの連携」にて検討したいと思います。

コレクションのインジェクション

例えば、MapやListのような、総称型を含む型をインジェクションしようと思った場合、基本的には文字列のような単純な型と同じなのですが、ちょっぴりだけ難しくなります。例として、Mapをインジェクションするためのサンプルコードを以下に示します。

  • Mapをインジェクションされるクラスのインターフェース
public interface MapInjectedService {
    public void execute();
}
  • Mapをインジェクションされるクラスの実装
public class MapInjectedServiceImpl implements MapInjectedService {
    @Inject
    @Named("map")
    private Map<String, String> map_;

    public void execute() {
        for (String key : map_.keySet()) {
            String value = map_.get(key);
            System.out.println("key : " + key + ", value : " + value);
        }
    }
}
  • モジュールクラス
public class MapInjectorModule extends AbstractModule {
    @Override
    protected void configure() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("key1", "value1");
        map.put("key2", "value2");
        map.put("key3", "value3");
        map.put("key4", "value4");
        
        bind(new TypeLiteral<Map<String, String>>(){}).annotatedWith(Names.named("map")).toInstance(map);
        bind(MapInjectedService.class).to(MapInjectedServiceImpl.class).in(Scopes.SINGLETON);
    }
}
  • テスト用クラス
public class MapInjectTest {
    public static void main(String[] args) {
        Module module = new MapInjectorModule();
        Injector injector = Guice.createInjector(module);
        MapInjectedService service = injector.getInstance(MapInjectedService.class);
        service.execute();
    }
}
  • 実行結果
key : key1, value : value1
key : key3, value : value3
key : key2, value : value2
key : key4, value : value4


「TypeLiteral」を使っているところがミソですね。