Scope
今回はScope。といってもSingletonにするってだけですが。
以下みたいなコードに対して Singletonを実現したい!って場合
Client client1 = injector.getInstance(Client.class);
// どこか別の場所
Client client2 = injector.getInstance(Client.class);
Guiceでは特に指定しない限りはこれがデフォルトの動作で getInstance()される度にインスタンスを作る。
インスタンスを全体で一つに制限してSingletonにしたい場合いくつかの方法がある。
一つ目は Singletonにしたい 実装に対して@Singletonアノテーションを追加します。
import com.google.inject.Singleton;
@Singleton
public class ServiceImpl implements Service {
@Override
public String getResponse(String message) {
return "ハイサイ!" + message;
}
}
簡単だけど、注意も必要。インターフェースの実装が複数ある場合、一部の実装だけアノテーションを忘れてしまう危険性がある。ので以下の inと併用すると良いかと思う。
// ServiceImplは Singletonと宣言
bind(ServiceImpl.class).in(Singleton.class);
// 注入をする
bind(Service.class).to(ServiceImpl.class);
以下のように一行でも書けるが この場合 「このbind関係だけSingleton」になるので注意
bind(ServiceInterface.class).to(ServiceImpl.class).in(Singleton.class);
例えば ServiceImplが複数のインターフェースを実装している場合、 以下のようなコードでは ServiceImplは Singletonとはならず、2つインスタンスが出来る
bind(ServiceInterface.class).to(ServiceImpl.class).in(Singleton.class);
bind(AnotherInterface.class).to(ServiceImpl.class).in(Singleton.class);
以前のソースを変更して Singletonで作られている事を確認
まずはSingletonである事の宣言
import com.google.inject.Singleton;
@Singleton
public class ServiceImpl implements Service {
@Override
public String getResponse(String message) {
return "ハイサイ!" + message;
}
}
続いて Clientの Serviceにアクセスできるように作られた オブジェクトの数を数えるのとServiceを比較できるようにgetterを追加。
import com.google.inject.Inject;
public class Client {
@Inject
private Service service;
private static int objCount = 0;
private final int number;
public Client() {
objCount++;
number = objCount;
System.out.println("Create instance " + number);
}
public void execute() {
System.out.println(service.getResponse("Guice" + number));
}
public Service getService() {
return service;
}
public int getNumber() {
return number;
}
}
最後に Mainを書き換えて Singletonかどうかを確認します。
import com.google.inject.Guice;
import com.google.inject.Injector;
public class Main {
public static void main(String[] args) {
// Injectorを作成する。この時にBindの設定を書いたModuleのInstanceを渡す。
Injector injector = Guice.createInjector(new OkinawaModule());
System.out.println("Injector is created.");
Client client1 = injector.getInstance(Client.class);
Client client2 = injector.getInstance(Client.class);
// 実行する。
client1.execute();
client2.execute();
// service の instanceはどうなった?
if (client1.getService() == client2.getService()) {
System.out.println("Service is singleton.");
} else {
System.out.println("Service is NOT singleton.");
}
}
}
実行結果
$ java -cp di.jar;. Main
Injector is created.
Create instance 1
Create instance 2
ハイサイ!Guice1
ハイサイ!Guice2
Service is singleton.
クライアントは複数いますが、ServiceImplはインスタンスが一つしか作成されず Singletonになっています。
ServiceImpl.javaの @Singletonをはずして、OkinawaModule.javaを以下の様に書き換えても同じ様な結果が得られる。
import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
public class OkinawaModule extends AbstractModule {
@Override
protected void configure() {
bind(ServiceImpl.class).in(Singleton.class);
bind(Service.class).to(ServiceImpl.class);
}
}
Singletonの初期化タイミングについては また今度