Guice vs Spring –依存性注入

1. 前書き

link:/guice[_Google Guice_]およびlink:/spring-tutorial[_Spring_]は、依存性注入に使用される2つの堅牢なフレームワークです。 両方のフレームワークは、依存性注入のすべての概念をカバーしていますが、それぞれに独自の実装方法があります。
このチュートリアルでは、GuiceフレームワークとSpringフレームワークの構成と実装の違いについて説明します。

2. Mavenの依存関係

まず、GuiceおよびSpring Mavenの依存関係を_pom.xml_ファイルに追加します。
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.4.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>4.2.2</version>
</dependency>
常に最新の__https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22org.springframework%22%20AND%20a%3A%22spring-context%22 [spring-context ] __or _https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22com.google.inject%22%20a%3Aguice [guice] _ Maven Centralからの依存関係。

3. 依存性注入の構成

link:/inversion-control-and-dependency-injection-in-spring[Dependency injection]は、クラスを依存関係から独立させるために使用するプログラミング手法です。
このセクションでは、SpringとGuiceで依存性注入を構成する方法が異なるいくつかのコア機能を参照します。

3.1. 春の配線

  • Springは、特別な構成クラスで依存性注入構成を宣言します。*このクラスには、_ @ Configuration_アノテーションを付ける必要があります。 Springコンテナは、このクラスをBean定義のソースとして使用します。

  • Springが管理するクラスは* * https://www.baeldung.com/spring-bean [Spring beans]と呼ばれます。*

  • Springは@Autowiredアノテーションをlink:/spring-annotations-resource-inject-autowire [依存関係を自動的に配線]に使用します* 。 _ @ Autowired_はSpringの組み込みコアアノテーションの一部です。 メンバー変数、セッターメソッド、およびコンストラクターで_ @ Autowired_を使用できます。

    Springは_ @ Injectもサポートしています。 @Inject_は、依存性注入の標準を定義するlink:/java-ee-cdi[Java CDI(コンテキストおよび依存性注入)]の一部です。
    依存関係をメンバー変数に自動的にワイヤリングしたいとしましょう。 単に_ @ Autowired_で注釈を付けることができます:
@Component
public class UserService {
    @Autowired
    private AccountService accountService;
}
@Component
public class AccountServiceImpl implements AccountService {
}
次に、アプリケーションコンテキストの読み込み中にBeanのソースとして使用する構成クラスを作成しましょう。
@Configuration
@ComponentScan("com.baeldung.di.spring")
public class SpringMainConfig {
}
また、__ UserService ___and_AccountServiceImpl_に_ @ Component_アノテーションを付けて、Beanとして登録していることに注意してください。 *注釈付きコンポーネントを検索する場所をSpringに指示するのは、_ @ ComponentScan_注釈です。
_AccountServiceImpl **に注釈を付けましたが、Springは_AccountService_を実装しているため、__ AccountService ___にマップできます。
次に、Beanにアクセスするためのアプリケーションコンテキストを定義する必要があります。 Springのすべての単体テストでこのコンテキストを参照することに注意してください。
ApplicationContext context = new AnnotationConfigApplicationContext(SpringMainConfig.class);
実行時に、_UserService_ beanから_AccountService_インスタンスを取得できます。
UserService userService = context.getBean(UserService.class);
assertNotNull(userService.getAccountService());

* 3.2。 Guice Binding *

  • Guiceは、a moduleと呼ばれる特別なクラスで依存関係を管理します。* Guiceモジュールは、AbstractModule classを拡張し、_configure()_メソッドをオーバーライドする必要があります。

    Guiceは、Springでの配線と同等のバインディングを使用します。 簡単に言えば、* bindingsを使用すると、依存関係をクラスに注入する方法を定義できます*。 Guiceバインディングは、モジュールの__configure()__methodで宣言されています。
  • Guiceは_ @ Autowired_の代わりに、@Inject注釈を使用して依存関係を注入します。 *

    同等のGuiceの例を作成しましょう。
public class GuiceUserService {
    @Inject
    private AccountService accountService;
}
次に、バインディング定義のソースであるモジュールクラスを作成します。
public class GuiceModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(AccountService.class).to(AccountServiceImpl.class);
    }
}
通常、_configure()_メソッドで明示的に定義されたバインディングがない場合、Guiceはデフォルトのコンストラクターから各依存関係オブジェクトをインスタンス化することを期待しています。 *ただし、インターフェイスは直接インスタンス化できないため、どのインターフェイスがどの実装とペアになるかをGuiceに伝えるために、バインディングを定義する必要があります。
次に、_GuiceModule_を使用して_Injector_を定義し、クラスのインスタンスを取得する必要があります。 すべてのGuiceテストでこの_Injector_が使用されることに注意してください。
Injector injector = Guice.createInjector(new GuiceModule());
最後に、実行時に、null以外の_accountService_ dependencyで_GuiceUserService_インスタンスを取得します。
GuiceUserService guiceUserService = injector.getInstance(GuiceUserService.class);
assertNotNull(guiceUserService.getAccountService());

3.3. Springの@Beanアノテーション

  • Springは、_ @ Component_のようなクラスレベルのアノテーションの代わりに、Beanを登録するためのメソッドレベルのアノテーション_ @ Bean_も提供します。 _ @ Bean_注釈付きメソッドの戻り値は、Beanとしてコンテナに登録されます。

    _BookServiceImpl_のインスタンスがあり、それをインジェクションに使用できるようにしたいとします。 _ @ Bean_を使用してインスタンスを登録できます。
@Bean
public BookService bookServiceGenerator() {
    return new BookServiceImpl();
}
これで、_BookService_ Beanを取得できます。
BookService bookService = context.getBean(BookService.class);
assertNotNull(bookService);

* 3.4。 Guiceの@Providesアノテーション*

Springの_ @ Bean_アノテーションに相当するものとして、* Guiceには組み込みのアノテーション__https://google.github.io/guice/api-docs/latest/javadoc/index.html?com / google / inject / Providesがあります。 html [@Provides] __同じ仕事をする*。 _ @ Bean_と同様に、_ @ Provides_はメソッドにのみ適用されます。
さて、前のSpring Beanの例をGuiceで実装しましょう。 次のコードをモジュールクラスに追加するだけです。
@Provides
public BookService bookServiceGenerator() {
    return new BookServiceImpl();
}
これで、_BookService_のインスタンスを取得できます。
BookService bookService = injector.getInstance(BookService.class);
assertNotNull(bookService);

* 3.5。 Spring *でのクラスパスコンポーネントスキャン

Springは、事前に定義されたパッケージをスキャンすることにより、注釈付きコンポーネントを自動的に検出およびインスタンス化するa ** _ @ ComponentScan_注釈を提供します。
_ @ ComponentScan_注釈は、注釈付きコンポーネントについてスキャンされるパッケージをSpringに指示します。 _ @ Configuration_注釈とともに使用されます。

* 3.6。 Guice *でのクラスパスコンポーネントスキャン

Springとは異なり、* Guiceにはそのようなコンポーネントスキャン機能がありません*。 しかし、それをシミュレートすることは難しくありません。 https://github.com/Netflix/governator[_Governator_]のようなプラグインがあり、この機能をGuiceに導入できます。

3.7. 春の物体認識

Springは名前でオブジェクトを認識します。 * Springは、ほぼ_Map <String、Object> _のような構造でオブジェクトを保持します。*これは、同じ名前の2つのオブジェクトを保持できないことを意味します。
*同じ名前の複数のBeanを持つことによるBeanの衝突は、1つの一般的な問題* Spring開発者がヒットします。 たとえば、次のBean宣言を考えてみましょう。
@Configuration
@Import({SpringBeansConfig.class})
@ComponentScan("com.baeldung.di.spring")
public class SpringMainConfig {
    @Bean
    public BookService bookServiceGenerator() {
        return new BookServiceImpl();
    }
}
@Configuration
public class SpringBeansConfig {
    @Bean
    public AudioBookService bookServiceGenerator() {
        return new AudioBookServiceImpl();
    }
}
覚えているように、_SpringMainConfig_ classには_BookService_のBean定義が既にありました。
ここでBeanの衝突を作成するには、同じ名前でBeanメソッドを宣言する必要があります。 ただし、1つのクラスに同じ名前の2つの異なるメソッドを含めることはできません。 そのため、別の構成クラスで_AudioBookService_ Beanを宣言しました。
では、ユニットテストでこれらのBeanを参照してみましょう。
BookService bookService = context.getBean(BookService.class);
assertNotNull(bookService);
AudioBookService audioBookService = context.getBean(AudioBookService.class);
assertNotNull(audioBookService);
ユニットテストは次のエラーで失敗します。
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'AudioBookService' available
最初に、Springは_AudioBookService_ BeanをそのBeanマップ内で_“bookServiceGeneratorâ_の名前で登録しました。 次に、_HashMap_データ構造の* _ "重複する名前は許可されない" _ *の性質により、_BookService_のBean定義によってそれをオーバーライドする必要がありました。
最後に、* Beanメソッド名を一意にするか、_name_属性を各_ @ Bean_の一意の名前に設定することで、この問題を解決できます。*

3.8. Guiceでのオブジェクト認識

Springとは異なり、* Guiceには基本的に__Map __ <Class <?>、Object>構造があります*。 これは、追加のメタデータを使用せずに同じタイプに複数のバインディングを設定できないことを意味します。
Guiceはhttps://github.com/google/guice/wiki/BindingAnnotations[binding annotations]を提供して、同じタイプに対して複数のバインディングを定義できるようにします。 Guiceの同じ型に対して2つの異なるバインディングがある場合に何が起こるか見てみましょう。
public class Person {
}
それでは、_Person_クラスに対して2つの異なるバインディングを宣言しましょう。
bind(Person.class).toConstructor(Person.class.getConstructor());
bind(Person.class).toProvider(new Provider<Person>() {
    public Person get() {
        Person p = new Person();
        return p;
    }
});
_Person_クラスのインスタンスを取得する方法は次のとおりです。
Person person = injector.getInstance(Person.class);
assertNotNull(person);
これは次の場合に失敗します。
com.google.inject.CreationException: A binding to Person was already configured at GuiceModule.configure()
_Person_クラスのバインディングの1つを単に破棄するだけで、この問題を克服できます。

3.9. Springのオプションの依存関係

link:/spring-autowire#dependencies [オプションの依存関係]は、Beanを自動配線またはインジェクトするときに必要ではない依存関係です。
_ @ Autowired_アノテーションが付けられたフィールドの場合、データ型が一致するBeanがコンテキストで見つからない場合、Springは_NoSuchBeanDefinitionException_をスローします。
ただし、一部の依存関係の自動配線をスキップして、例外をスローせずに_null_ *のままにしておくこともできます。
次の例を見てみましょう。
@Component
public class BookServiceImpl implements BookService {
    @Autowired
    private AuthorService authorService;
}
public class AuthorServiceImpl implements AuthorService {
}
上記のコードからわかるように、_AuthorServiceImpl_クラスにはコンポーネントとして注釈が付けられていません。 そして、設定ファイルにはそのためのBean宣言メソッドがないと仮定します。
それでは、次のテストを実行して、何が起こるかを見てみましょう。
BookService bookService = context.getBean(BookService.class);
assertNotNull(bookService);
驚くことではありませんが、次のように失敗します。
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'AuthorService' available
  • Java 8の_Optional_ typeを使用して_authorService_依存関係をオプションにすると、この例外を回避できます。*

public class BookServiceImpl implements BookService {
    @Autowired
    private Optional<AuthorService> authorService;
}
_authorService_依存関係は、_AuthorService_タイプのBeanを含む場合と含まない場合のコンテナーに似ています。 アプリケーションコンテキストに_AuthorService_のBeanはありませんが、_authorService_フィールドは空ではない空のコンテナーです。 したがって、Springには_NoSuchBeanDefinitionException_をスローする理由はありません。
_Optional_の代わりに、デフォルトで_true_に設定されている_ @Autowired_‘s _required_属性を使用して、依存関係をオプションにすることができます。 * _required_属性を_false_に設定して、自動配線の依存関係をオプションにすることができます。*
したがって、そのデータ型のBeanがコンテキストで利用できない場合、Springは依存関係の注入をスキップします。 依存関係は_null:_に設定されたままになります
@Component
public class BookServiceImpl implements BookService {
    @Autowired(required = false)
    private AuthorService authorService;
}
すべての依存関係が常に必要なわけではないため、依存関係をオプションとしてマークすると便利な場合があります。
これを念頭に置いて、_null_依存関係による_NullPointerException_を回避するために、開発中は特別な注意と_null_-checksを使用する必要があることを覚えておく必要があります。

3.10。 Guiceのオプションの依存関係

_Spring_と同様に、* _ Guice_はJava 8の_Optional_型を使用して依存関係をオプションにすることもできます。*
クラスを作成し、_Foo_依存関係を使用するとします。
public class FooProcessor {
    @Inject
    private Foo foo;
}
それでは、_Foo_クラスのバインディングを定義しましょう。
bind(Foo.class).toProvider(new Provider<Foo>() {
    public Foo get() {
        return null;
    }
});
ユニットテストで_FooProcessor_のインスタンスを取得してみましょう。
FooProcessor fooProcessor = injector.getInstance(FooProcessor.class);
assertNotNull(fooProcessor);
ユニットテストは次の場合に失敗します
com.google.inject.ProvisionException:
null returned by binding at GuiceModule.configure(..)
but the 1st parameter of FooProcessor.[...] is not @Nullable
この例外をスキップするには、単純な更新で_foo_依存関係をオプションにできます。
public class FooProcessor {
    @Inject
    private Optional<Foo> foo;
}
_ @ Inject_には、依存関係をオプションとしてマークする_required_属性がありません。 * Guiceで依存関係をオプションにする*代替方法は、_ @ Nullable_アノテーションを使用することです*
Guiceは、上記の例外メッセージで表されている_ @ Nullable_を使用する場合に_null_値を挿入することを許容します。 _ @ Nullable_アノテーションを適用しましょう:
public class FooProcessor {
    @Inject
    @Nullable
    private Foo foo;
}

4. 依存性注入タイプの実装

このセクションでは、https://www.baeldung.com/inversion-control-and-dependency-injection-in-spring [依存性注入タイプ]を見て、SpringとGuiceが提供する実装を比較します。いくつかの例を見ていきます。

4.1. 春のコンストラクター注入

  • https://www.baeldung.com/constructor-injection-in-spring [コンストラクターベースの依存性注入]では、インスタンス化時に必要な依存性をクラスに渡します。*

    Springコンポーネントが必要であり、そのコンストラクターを通じて依存関係を追加するとします。 そのコンストラクタに_ @ Autowired_で注釈を付けることができます:
@Component
public class SpringPersonService {

    private PersonDao personDao;

    @Autowired
    public SpringPersonService(PersonDao personDao) {
        this.personDao = personDao;
    }
}
Spring 4以降、クラスにコンストラクターが1つしかない場合、このタイプの注入には_ @ Autowired_依存関係は必要ありません。
テストで_SpringPersonService_ Beanを取得しましょう:
SpringPersonService personService = context.getBean(SpringPersonService.class);
assertNotNull(personService);

* 4.2。 Guiceでのコンストラクター注入*

前の例を再配置して、Guiceでコンストラクター注入を実装できます。 Guiceは_ @ Autowired_の代わりに_ @ Inject_を使用することに注意してください。
public class GuicePersonService {

    private PersonDao personDao;

    @Inject
    public GuicePersonService(PersonDao personDao) {
        this.personDao = personDao;
    }
}
テストで_injector_から_GuicePersonService_ classのインスタンスを取得する方法は次のとおりです。
GuicePersonService personService = injector.getInstance(GuicePersonService.class);
assertNotNull(personService);

4.3. Springでのセッターまたはメソッドインジェクション

*セッターベースの依存性注入では、コンストラクターを呼び出してコンポーネントをインスタンス化した後、コンテナーはクラスのセッターメソッドを呼び出します。*
Springに、setterメソッドを使用して依存関係を自動配線させたいとしましょう。 そのセッターメソッドに_ @ Autowired_で注釈を付けることができます。
@Component
public class SpringPersonService {

    private PersonDao personDao;

    @Autowired
    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }
}
_SpringPersonService_クラスのインスタンスが必要になるたびに、Springは_setPersonDao()_メソッドを呼び出して_personDao_ fieldを自動配線します。
次のように、テストで_SpringPersonService_ Beanを取得し、その_personDao_フィールドにアクセスできます。
SpringPersonService personService = context.getBean(SpringPersonService.class);
assertNotNull(personService);
assertNotNull(personService.getPersonDao());

4.4。 ** Guiceでのセッターまたはメソッドインジェクション

Guiceで* setterインジェクションを実現するために、サンプルを少し変更するだけです。
public class GuicePersonService {

    private PersonDao personDao;

    @Inject
    public void setPersonDao(PersonDao personDao) {
        this.personDao = personDao;
    }
}
インジェクターから_GuicePersonService_ classのインスタンスを取得するたびに、上記のsetterメソッドに_personDao_ fieldが渡されます。
テストで_GuicePersonService_クラスのインスタンスを作成し、その_personDao_ field __にアクセスする方法を次に示します。
GuicePersonService personService = injector.getInstance(GuicePersonService.class);
assertNotNull(personService);
assertNotNull(personService.getPersonDao());

4.5. 春のフィールドインジェクション

すべての例で、SpringとGuiceの両方にフィー​​ルドインジェクションを適用する方法を既に見ました。 したがって、それは私たちにとって新しい概念ではありません。 しかし、完全を期すためにもう一度リストに入れましょう。
*フィールドベースの依存性注入の場合、依存関係を_ @ Autowired_ or _ @ Inject_でマークして注入します。*

* 4.6。 Guiceでのフィールドインジェクション*

上記のセクションで述べたように、すでに_ @ Inject_ *を使用したGuiceのフィールドインジェクションについて説明しました。

5. 結論

このチュートリアルでは、GuiceフレームワークとSpringフレームワークのいくつかの重要な違いを、依存性注入の実装方法について検討しました。 いつものように、https://github.com/eugenp/tutorials/tree/master/guice [Guice]およびhttps://github.com/eugenp/tutorials/tree/master/spring-di[Spring]コードサンプルは終了しましたGitHubで。