レベルエンター山本大のブログ

面白いプログラミング教育を若い人たちに

BLOCKVROCKリファレンス目次はこちら

Guice vs. SpringJavaConfig (1) A Knight's tour of Guice

Guice vs. Spring JavaConfig: A comparison of DI styles

DIスタイルの比較:Guice 対 SpringのJavaコードコンフィグ

http://jroller.com/page/habuma?entry=guice_vs_spring_javaconfig_a

With all of the excitement surrounding Guice lately, I thought it might be worthwhile to compare Guice with Spring JavaConfig. Both represent different approaches to annotation-based dependency-injection. I've chosen to reimplement my knight/quest example from chapter 1 of Spring in Action to illustrate the basics of each option.

近頃、Guiceを取り巻く話題が熱いです!そこで私はGuiceとSpringのJavaコードコンフィグを比較するのはやりがいがあるんじゃないかと考えました!
この2つはどちらも、異なるアプローチのアノテーションベースDependency-Injectionを実現します。
この記事ではそれぞれの基本的な機能を説明するために「Spring In Action」のチャプター1から「 knight/quest 」サンプルを再実装することにします。


Before I get started, a few disclaimers:

  • As the author of Spring in Action, it should be no surprise to you that I will have a difficult time being unbiased. Therefore, I will make no pretense of impartiality. However, I will do my best to be as fair as I can be to both Guice and Spring JavaConfig.
  • As someone who has several years of Spring experience to match his few days of Guice experience, I will admit that my knowledge of Guice isn't up to par with my knowledge of Spring. Therefore, it is quite possible that there are things about Guice that I simply do not know about and have not taken advantage of in my comparison. I assure you that failure to use the best possible Guice technique is not meant to devalue Guice, but merely due to the fact that I'm not as experienced as Guice as I am with Spring. For what it's worth, while I have several years of Spring experience, I'm still relatively new to Spring JavaConfig.
  • This is only a comparison of Guice with Spring JavaConfig, the core Spring container, and (to a lesser degree) Spring AOP; it is not a comparison of Guice to the entire Spring Framework.The Spring Framework is much more than just its core dependency injection container, providing functionality that Guice does not provide or claim to provide.
  • My comparisons will with Guice 1.0, Spring JavaConfig 1.0-m1, and Spring 2.0.2.

With that out of the way, let's get started. Since Spring is the hometeam in this matchup, I'll let Guice take the field first.


始める前に、次のいくつかの免責事項を表明します

  • 「Spring in Action」の著者である著者が、公平になることはなかなか難しいということをご理解ください。そのため私は公平なフリをするつもりはありません。しかしながら、GuiceとSpring JavaCondfigの両方に対して、できるだけ公平であるように勤めます。
  • Springが数年の実績を持っているのに対して、Guiceはたかだか数日の実績しかありません。私のGuiceの知識はSpringに対する知識には遠く及びません。そのため 私がGuiceに関して知らないために比較で利用できないものがあることは非常にありえます。私にGuiceのテクニックを最大限つかえはしないということを断言できます。しかしそれは、Guiceの価値が下がると言うことを意味してはいません。ただ単に私がSpringほどにはGuiceを使ったことがないと言うだけの問題です。これはあくまでも私の意見ですが、数年Springを経験してきましたが、私はまだSpring JavaConfigに比較的経験が少ないほうです。
  • この記事は単なるGuiceとSpringのコアコンテナと(より少ない度合いで)Spring AOPに対するSpring JavaConfigの比較です。GuiceとすべてのSpring Frameworkの比較ではありません。Spring FrameWorkは、コアであるDIコンテナだけではなく、Guiceが提供または要求していない、より多くの機能を提供するものです。
  • 私の比較は、Guice1.0とSpring JavaConfig 1.0-m1およびSpring 2.0.2によるものです。

そんなところで、はじめていきましょう。 Springがこの比較の「地元チーム」なので、まずはGuiceからグラウンドに出して行きましょう。

A Knight's tour of Guice Gucieにおける騎士ツアー

In Guice, much of the configuration is done in the context of a module class. This class effectively binds classes to interfaces within the Guice injector, so that Guice knows how to resolve certain dependencies. Here's what the KnightModule class that I wrote looks like:


Guiceでは、設定のほとんどは、モジュールクラスのコンテキストで行われます。このクラスは、Guiceインジェクターを使って、効果的にクラスをインターフェイスにバインドします。これによってGuceiが依存性を解決する方法をしることができるようになります。以下は、私が作った「KnightModule」クラスです。

package com.springinaction.chapter01.knight;
import static com.google.inject.matcher.Matchers.annotatedWith;
import static com.google.inject.matcher.Matchers.any;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Scopes;

public class KnightModule extends AbstractModule {
  public void configure() {
    
    bindConstant().annotatedWith(Name.class)
        .to("Bedivere");
    
    bind(Knight.class).to(KnightOfTheRoundTable.class)
        .in(Scopes.SINGLETON);

    bind(Quest.class).to(HolyGrailQuest.class)
        .in(Scopes.SINGLETON);
    
    bindInterceptor(
      any(), 
      annotatedWith(MinstrelIntercepted.class), 
      new MinstrelInterceptor()
    );
  }
  
  public static void main(String[] args) throws Exception {
    Injector injector = Guice.createInjector(new KnightModule() );
    Knight knight = injector.getInstance(Knight.class);
    knight.embarkOnQuest();
  }
}

(Note that for space considerations I'm only going to show the code that is pertinent to the comparison of Guice with Spring JavaConfig. The full source code for the Guice examples will be available here, while you'll be able to find the Spring JavaConfig examples here. Also notice that I've placed the main() method in KnightModule for convenience only--you may or may not want to do this in a real-world application.)


(スペースへの配慮のために、GuiceとSpring JavaConfigの比較に必要なコードだけを表示しますので注意してください。Guiceのサンプルのすべてのソースコードは、ここで入手できます。また、実際のアプリケーションではそんなことはやらないかもしれませんが、便利であるというためだけにKnightModukeの中にmain()メソッドを置いてることにも注意してください。)

The first thing you'll see is the configure() method. This method, which is mandated by the AbstractModule class, is used for, among other things, binding interfaces to an implementation classes. This is important, because in Guice, you do not explicitly wire objects together--instead you declare that a property, constructor argument, or even a method argument needs to be injected and Guice will automatically (or semi-automatically, to be more accurate) find an implementation to match the property or argument.


初めに見ていくのは、configure()メソッドについてです。AbstractModuleによって委任統治に指定されたこのメソッドは、特に実装クラスのためのバインディングインターフェイスを代用します。
これは、重要なことです。なぜならGucieでは、明示的にオブジェクトを紐付けできないのです。変わりにプロパティーやコンストラクタ引数、またはメソッド引数でさえ、
インジェクトされることが必要であると宣言します。そして、Guiceが宣言したプロパティーや引数に一致する実装を自動的に(正確に言えば半自動的に)見つけます。

For example, notice that the Knight interface is bound to the KnightOfTheRoundTable class. What this is basically telling Guice is that anytime it needs to inject a property or argument that is of type Knight, it should use the concrete KnightOfTheRoundTable implementation. Similarly, the Quest interface is bound to the HolyGrailQuest class. Thus when KnightOfTheRoundTable needs its quest property wired with a Quest implementation, Guice will oblige with a HolyGrailQuest.


たとえば、Kightインターフェイスは、KnightOfTheRoundTable クラスにバインドされています。これは基本的に、GuiceはいつでもKight型のプロパティーや引数にインジェクトするひつようがあるということを意味しています。具体的なKnightOfTheRoundTable実装を使うべきです。同じようにQuestインターフェイスは、HolyGrailQuestクラスがバインドされています。
そのため、KnightOfTheRoundTableが、questプロパティーにQuestの実装を紐付けようとしたとき、GuiceはHolyGrailQuestを強制的に結び付けます。

Along with being wired with a Quest implementation, the KnightOfTheRoundTable class is also constructed with a String value that is the knight's name. Unfortunately, I could find no more straightforward way of wiring a String value into KnightOfTheRoundTable's constructor without binding the value through an annotation. Thus, the call to bindConstant() tells Guice to inject the value "Bedivere" into any String that is annotated with @Name (which is a custom annotation that I created as an "anchor" to bind the String injection to).


Questの実装との紐付けをはじめるに従って、KnightOfTheRoundTableクラスも騎士の名前である文字列値を使って構築されます。残念ながら、アノテーションをつかって値をバインドするというほう法を使う以外、もっと素直にKnightOfTheRoundTableのコンストラクタに文字列の値を結びつける方法を見つけることができませんでした。
こんなふうに、bindConstant()を呼びだして、Guiceに対して、すべての@Name(※文字列にインジェクトするためのアンカーとして作ったカスタムアノテーション)というアノーテーションがつけられたすべての文字列に"Bedivere"という値をインジェクトするように伝えます。

In the case of both the Quest and Knight bindings, the in() method is called to set the scoping of the bean to be singleton. Optionally, you can leave this out and instead place a @Singleton annotation on the KnightOfTheRoundTable and HolyGrailQuest classes. However, for reasons I'll describe later in this article, I don't particularly recommend the @Singleton annotation.


QuestとKnightのバインディングの両方の件では、in()メソッドがシングルトンにするためのBeanの範囲付けをセットするために呼び出されます。任意で、この方法を除外して、KnightOfTheRoundTableクラスとHolyGrailQuestクラスにて、@Singletonアノテーションを使うことで代用することができます。
しかしながら、この記事の後の方で説明する理由から、私は特に@Singletonアノテーションを推奨します。

I'll come back to the call to bindInterceptor() in a moment. But first, it's important to know that KnightModule only tells Guice about the objects and values that it is to inject. It doesn't say where to inject them. For that, I had to place annotations in the KnightOfTheRoundTable class. Here's the modified KnightOfTheRoundTable.java:


すぐに、bindInterceptor()を呼び出すところに戻ってきました。しかしまずは、KnightModuleがGuiceに対して、インジェクトの対象のオブジェクトと値について唯一教えているところについて知ることが重要です。
ここでは、それらをインジェクトするためにとは言いません。という理由で、KnightOfTheRoundTable クラスにアノテーションを置く必要があります。ここでは、KnightOfTheRoundTable.javaファイルを書き換えます。

package com.springinaction.chapter01.knight;

import com.google.inject.Inject;

public class KnightOfTheRoundTable implements Knight {
  private String name;
  private Quest quest;
  
  @Inject
  public KnightOfTheRoundTable(<b>@Name</b> String name) {
    this.name = name;
  }
  
  public Object embarkOnQuest() throws QuestFailedException {
    return quest.embark();
  }
  
  <b>@Inject</b>
  public void setQuest(Quest quest) {
    this.quest = quest;
  }
  
  public String getName() {
    return name;
  }
}


Notice the use of the @Inject annotation on both the constructor and the setQuest() method. This annotation tells Guice that it needs to inject some values into the arguments of the constructor and method that it annotates. Without this annotation in place, Guice will pass over those methods and not perform any injection at all.


@Injectアノテーションがコンストラクタと、setQuest()メソッドの両方に使われていることに注意してください。このアノテーションGuiceに対して、アノテーションがつけられたコンストラクタ引数や、メソッドに、なんらかの値をインジェクトする必要があることを教えています。
このアノーテーションがなければ、Guiceはそのメソッドを見逃して、なにもインジェクトしません。

First look at the setQuest() method. It is annotated with @Inject and has a Quest argument. As you'll recall, Quest is bound to HolyGrailQuest. Therefore, when Guice is configuring the KnightOfTheRoundTable object, it will call setQuest(), passing in a reference to a HolyGrailQuest object.


初めに見るのは、setQuest()メソッドです。これは@Injectでアノテートされていて、Quest型の引数を持っています。思い出してください、QuestはHolyGrailQuestにバインドされています。そのため、GuiceがKnightOfTheRoundTable オブジェクトを設定したときには、setQuest()メソッドが呼び出され、HolyGrailQuestオブジェクトの参照が渡されます。

It should also be noted that although this example doesn't show it, Guice can also inject into any method, not just setters and constructors. So, even if the setQuest() method were renamed to giveMeAQuestPlease(), Guice would still call it, passing in a HolyGrailQuest object.


Guiceはセッターやコンストラクタのみならずどんなメソッドにもインジェクトできることを、このサンプルでは表せていないことも注意していただかなくてはいけません。そのため、setQuest()メソッドが、giveMeAQuestPlease()と改名されたとしても、Guiceは呼び出してHolyGrailQuestオブジェクトを引き渡せます。

Also, instead of explicitly binding Quest to HolyGrailQuest in KnightModule.java, I could've used the @ImplementedBy annotation in Quest.java to bind HolyGrailQuest to Quest. For example, here's the Quest interface, annotated with @ImplementedBy:


KnightModule.javaでの、Questに対するHolyGrailQuestの明示的なバインディングの代わりに、HolyGrailQuestをQuestにバインドするためにQuest.javaファイルで@ImplementedByアノテーションを使うこともできます。たとえば、次の例では、Questインターフェイスは@ImplementedByを使ってアノテートされています。

package com.springinaction.chapter01.knight;
import com.google.inject.ImplementedBy;

@ImplementedBy(HolyGrailQuest.class)
public interface Quest {
  public Object embark() throws QuestFailedException;
}

The @ImplementedBy annotation is effectively equivalent to the bind() call I used in KnightModule. The difference is that it sets up Quest to be bound to a default implementation class, should one not be bound to Quest in KnightModule.


@ImplementedByというアノテーションは、KnightModuleでつかったbind()メソッドを呼び出すことと同等の効果をもっています。違いは、起動するときにKnightModuleのQuestに対して何もバインドされなかったときに、Questにデフォルトの実装クラスをバインドすることです。

Now look at the constructor. It is also annotated with @Inject. And it has a String argument that is annotated with @Name. The @Inject argument tells Guice that the constructor must be injected, while the @Name annotation (a custom annotation that I created to help this example along) helps Guice know what to inject it with. Back in the KnightModule class, we bound the String value "Bedivere" to the @Name annotation. Therefore, when KnightOfTheRoundTable is constructed, it will be constructed with the argument being set to "Bedivere".


さて、コンストラクターを見てください。これも@Injectでアノテートされています。そして、@Nameでアノテートされた文字列の引数をもっています。
@Inejctアノテーションは、Guiceにコンストラクタがインジェクトされるべきであることを教えます。と同時に、@Nameアノテーション(このサンプルを補助するためにつくったカスタムアノテーション)は、Guiceがそこに何をインジェクトするかを知ることを助けます。KnightModuleクラスでは、@Nameアノテーションに"Bedivere"という文字列値をバインドしました。そのため、KnightOfTheRoundTableが組み立てられると、引数に"Bedivere"がセットされてコンストラクタが起動されます。


・・・つづく