guiceの基本的なインジェクション
はじめに
- 当ページは、「google guiceをベースにWebアプリケーションを作ってみよう」の1コンテンツです。
- google guiceを使った初歩的なDIについて、Spring Frameworkのそれと比較しながらまとめています。
前提
こんな感じのクラスを作成するとします。
サービスのインターフェース | Service |
サービスの実装クラス | ServiceImpl |
サービスをインジェクションされるクラスのインターフェース | Client |
サービスをインジェクションされるクラスの実装 | ClientImpl |
サービスをインジェクションされたClientを利用するクラス | Test |
サービスとクライアントのコード
こんな感じです。
- Service
public interface Service { public void execute(); }
- ServiceImpl
public class ServiceImpl implements Service { public void execute() { System.out.println("Service is executed."); } }
- Client
public interface Client { public void executeService(); }
- ClientImpl
public class ClientImpl implements Client { private Service service_; public void setService(Service service) { service_ = service; } public void executeService() { System.out.println("Client is executed."); service_.execute(); } }
Springの場合
guiceでDIする前に、比較として、デファクトなSpringでDIしてみたいと思います。
まず、Springの場合は、依存性を解決するためには以下のようなBean定義ファイルを作成する必要があります。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" (略) > <bean id="service" class="org.chiba.impl.ServiceImpl" /> <bean id="client" class="org.chiba.impl.ClientImpl"> <property name="service" ref="service" /> </bean> </beans>
上記のファイルは、とりあえずクラスパス直下に「applicationContext.xml」というファイル名で保存します。上記のBean定義ファイルを使って、Clientを通してServiceを実行するためのTestクラスのコードは以下のようになります。
public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Client client = (Client)context.getBean("client"); client.executeService(); } }
実行結果は以下のようになります。
Client is executed. Service is executed.
TestはClientImplに直接依存していませんし、ClientImplはServiceImplに直接依存していませんが、それぞれClientImplとServiceImplのメソッドが実行されていることが分かると思います。
Guiceの場合
上記のことをGuiceで行う場合にはまず、DI対象のsetterに「@Inject」アノテーションを付加する必要があります。
public class ClientImpl implements Client { private Service service_; @Inject ←←←←← ココ public void setService(Service service) { service_ = service; } public void executeService() { System.out.println("Client is executed."); service_.execute(); } }
次に、Bean定義ファイルの代わりに、「モジュール」と呼ばれるクラスを「AbstractModule」を継承して作成する必要があります。
public class TestModule extends AbstractModule { @Override protected void configure() { bind(Service.class).to(ServiceImpl.class).in(Scopes.SINGLETON); bind(Client.class).to(Client.class).in(Scopes.SINGLETON); } }
これらを利用するTestクラスのコードは以下のようになります。
public class Test { public static void main(String[] args) { Module module = new TestModule(); Injector injector = Guice.createInjector(module); Client client = injector.getInstance(Client.class); client.executeService(); } }
実行結果は以下のようになります。
Client is executed. Service is executed.
当然ですけど、Springの場合と同じですね。
比較
DIの設定をXMLで書くかコードで書くかは好みの分かれるところ*1ではあると思いますが、
- Spring
- Client client = (Client)context.getBean("client");
- Guice
- Client client = injector.getInstance(Client.class);
キャストや文字列リテラルを用いなくてもよい(IDEの補完が効く)のはGuice側のメリットだと思います。一方、Springの場合はClientやServiceはSpring自身にも依存していないのに対して、Guiceの場合は
@Inject public void setService(Service service) { service_ = service; }
ここでClientがGuice自身に依存してしまっているのが残念というかデメリットと言えばデメリットだと思います。