GuicevsSpring–依存性注入
1. 序章
GoogleGuiceとSpringは、依存性注入に使用される2つの堅牢なフレームワークです。 どちらのフレームワークも依存性注入のすべての概念をカバーしていますが、それぞれに独自の実装方法があります。
このチュートリアルでは、GuiceフレームワークとSpringフレームワークの構成と実装の違いについて説明します。
2. Mavenの依存関係
GuiceとSpringMavenの依存関係を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>
MavenCentralから常に最新のspring-contextまたはguiceの依存関係にアクセスできます。
3. 依存性注入の構成
依存性注入は、クラスを依存性から独立させるために使用するプログラミング手法です。
このセクションでは、依存性注入の構成方法がSpringとGuiceで異なるいくつかのコア機能について説明します。
3.1. 春の配線
Springは、特別な構成クラスで依存性注入構成を宣言します。このクラスには、@Configurationアノテーションを付ける必要があります。 Springコンテナは、このクラスをBean定義のソースとして使用します。
Springが管理するクラスは 春の豆。
Springは、@ Autowiredアノテーションを使用して、依存関係を自動的にワイヤリングします。 @Autowired は、Springの組み込みコアアノテーションの一部です。 @Autowired は、メンバー変数、セッターメソッド、およびコンストラクターで使用できます。
Springもサポートしています
依存関係をメンバー変数に自動的に結び付けたいとしましょう。 @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とAccountServiceImplにも@Componentの注釈を付けて、Beanとして登録していることに注意してください。 アノテーション付きコンポーネントをで検索する場所をSpringに指示するのは@ComponentScanアノテーションです。
AccountServiceImplに注釈を付けましたが、 Springは、 AccountService を実装しているため、AccountServiceにマップできます。
次に、Beanにアクセスするためのアプリケーションコンテキストを定義する必要があります。 すべてのSpring単体テストでこのコンテキストを参照することに注意してください。
ApplicationContext context = new AnnotationConfigApplicationContext(SpringMainConfig.class);
これで、実行時に、UserServiceBeanからA ccountServiceインスタンスを取得できます。
UserService userService = context.getBean(UserService.class);
assertNotNull(userService.getAccountService());
3.2. Guiceバインディング
Guiceは、モジュールと呼ばれる特別なクラスで依存関係を管理します。 Guiceモジュールは、 AbstractModule クラスを拡張し、 configure()メソッドをオーバーライドする必要があります。
Guiceは、Springの配線に相当するものとしてバインディングを使用します。 簡単に言うと、バインディングを使用すると、依存関係をクラスに注入する方法を定義できます。 Guiceバインディングは、モジュールの configure()メソッドで宣言されます。
@Autowired の代わりに、Guiceは@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 を使用してインジェクターを定義し、クラスのインスタンスを取得する必要があります。 すべてのGuiceテストでこのインジェクターが使用されることに注意してください。
Injector injector = Guice.createInjector(new GuiceModule());
最後に、実行時に、null以外のaccountService依存関係を持つGuiceUserServiceインスタンスを取得します。
GuiceUserService guiceUserService = injector.getInstance(GuiceUserService.class);
assertNotNull(guiceUserService.getAccountService());
3.3. Springの@Beanアノテーション
Springは、 @Component のようなクラスレベルのアノテーションの代わりに、beansを登録するためのメソッドレベルのアノテーション@Beanも提供します。 @Bean 注釈付きメソッドの戻り値は、Beanとしてコンテナーに登録されます。
インジェクションに使用できるようにしたいBookServiceImplのインスタンスがあるとします。 @Bean を使用して、インスタンスを登録できます。
@Bean
public BookService bookServiceGenerator() {
return new BookServiceImpl();
}
これで、BookServiceBeanを取得できます。
BookService bookService = context.getBean(BookService.class);
assertNotNull(bookService);
3.4. Guiceの@Providesアノテーション
Springの@Bean アノテーションに相当するものとして、Guiceには同じジョブを実行するためのアノテーション@Providesが組み込まれています。 @Bean と同様に、@Providesはメソッドにのみ適用されます。
次に、前のSpringbeanの例をGuiceで実装しましょう。 モジュールクラスに次のコードを追加するだけです。
@Provides
public BookService bookServiceGenerator() {
return new BookServiceImpl();
}
これで、BookServiceのインスタンスを取得できます。
BookService bookService = injector.getInstance(BookService.class);
assertNotNull(bookService);
3.5. Springでのクラスパスコンポーネントスキャン
Springは、 @ComponentScanアノテーションを提供し、事前定義されたパッケージをスキャンすることにより、アノテーションが付けられたコンポーネントを自動的に検出してインスタンス化します。
@ComponentScan アノテーションは、アノテーションが付けられたコンポーネントについてスキャンされるパッケージをSpringに通知します。 @Configurationアノテーションとともに使用されます。
3.6. Guiceでのクラスパスコンポーネントスキャン
Springとは異なり、Guiceにはそのようなコンポーネントスキャン機能はありません。 しかし、それをシミュレートすることは難しくありません。 この機能をGuiceに導入できるGovernatorのようなプラグインがいくつかあります。
3.7. 春の物体認識
Springはオブジェクトを名前で認識します。 Springは、おおよそマップのような構造でオブジェクトを保持します
同じ名前の複数の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クラスにはBookServiceのBean定義がすでにあります。
ここでBeanの衝突を作成するには、同じ名前のBeanメソッドを宣言する必要があります。 ただし、1つのクラスに同じ名前の2つの異なるメソッドを含めることは許可されていません。 そのため、別の構成クラスでAudioBookServiceBeanを宣言しました。
それでは、単体テストでこれらの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はAudioBookServiceBeanをそのBeanマップに「bookServiceGenerator」名で登録しました。 次に、 HashMap データ構造の「重複する名前は許可されていません」の性質のため、BookServiceのBean定義でオーバーライドする必要がありました。
最後に、 Beanメソッド名を一意にするか、name属性を@Beanごとに一意の名前に設定することでこの問題を解決できます。
3.8. Guiceでのオブジェクト認識
春とは異なり、 Guiceには基本的にマップがあります
Guiceは、バインディングアノテーションを提供して、同じタイプの複数のバインディングを定義できるようにします。 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のオプションの依存関係
オプションの依存関係は、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のオプションタイプを使用して、authorService依存関係をオプションにすることができます。
public class BookServiceImpl implements BookService {
@Autowired
private Optional<AuthorService> authorService;
}
現在、 authorService の依存関係は、AuthorServiceタイプのBeanを含む場合と含まない場合があるコンテナーに似ています。 アプリケーションコンテキストにAuthorServiceのbeanがない場合でも、authorServiceフィールドはnull以外の空のコンテナーのままです。 したがって、SpringにはNoSuchBeanDefinitionExceptionをスローする理由はありません。
オプションの代わりに、@Autowiredのrequired属性を使用できます。これはデフォルトでtrueに設定されています。オプションの依存関係。 必須属性をfalseに設定して、自動配線の依存関係をオプションにすることができます。
したがって、そのデータ型のBeanがコンテキストで使用できない場合、Springは依存関係の挿入をスキップします。 依存関係はnull:に設定されたままになります
@Component
public class BookServiceImpl implements BookService {
@Autowired(required = false)
private AuthorService authorService;
}
すべての依存関係が常に必要なわけではないため、依存関係をオプションとしてマークすると便利な場合があります。
これを念頭に置いて、nullの依存関係によるNullPointerExceptionを回避するために、開発中に特別な注意とnullチェックを使用する必要があることを覚えておく必要があります。 。
3.10. Guiceのオプションの依存関係
Spring と同様に、GuiceもJava8のオプションタイプを使用して依存関係をオプションにすることができます。
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属性がありません。 make をGuiceでオプションの依存関係にする別のアプローチは、@Nullableアノテーションを使用することです。
上記の例外メッセージで示されているように、 @Nullable を使用する場合、Guiceはnull値の挿入を許容します。 @Nullableアノテーションを適用してみましょう。
public class FooProcessor {
@Inject
@Nullable
private Foo foo;
}
4. 依存性注入タイプの実装
このセクションでは、依存性注入タイプを見て、SpringとGuiceが提供する実装をいくつかの例で比較します。
4.1. 春のコンストラクター注入
コンストラクターベースの依存性注入では、インスタンス化時に必要な依存性をクラスに渡します。
Springコンポーネントが必要であり、そのコンストラクターを介して依存関係を追加するとします。 そのコンストラクターに@Autowiredで注釈を付けることができます。
@Component
public class SpringPersonService {
private PersonDao personDao;
@Autowired
public SpringPersonService(PersonDao personDao) {
this.personDao = personDao;
}
}
Spring 4以降、クラスにコンストラクターが1つしかない場合、このタイプのインジェクションには@Autowired依存関係は必要ありません。
テストでSpringPersonServicebeanを取得してみましょう。
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;
}
}
テストでインジェクターからGuicePersonServiceクラスのインスタンスを取得する方法は次のとおりです。
GuicePersonService personService = injector.getInstance(GuicePersonService.class);
assertNotNull(personService);
4.3. 春のセッターまたはメソッド注入
セッターベースの依存性注入では、コンテナーは、コンストラクターを呼び出してコンポーネントをインスタンス化した後、クラスのセッターメソッドを呼び出します。
Springがsetterメソッドを使用して依存関係を自動配線したいとします。 そのセッターメソッドに@Autowiredで注釈を付けることができます。
@Component
public class SpringPersonService {
private PersonDao personDao;
@Autowired
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
}
SpringPersonService クラスのインスタンスが必要な場合は常に、Springは setPersonDao()メソッドを呼び出してpersonDaoフィールドを自動配線します。
SpringPersonService Beanを取得し、次のテストでそのpersonDaoフィールドにアクセスできます。
SpringPersonService personService = context.getBean(SpringPersonService.class);
assertNotNull(personService);
assertNotNull(personService.getPersonDao());
4.4. Guiceでのセッターまたはメソッドインジェクション
Guice でセッターインジェクションを実現するために、例を少し変更します。
public class GuicePersonService {
private PersonDao personDao;
@Inject
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
}
インジェクターからGuicePersonServiceクラスのインスタンスを取得するたびに、personDaoフィールドが上記のsetterメソッドに渡されます。
テストでGuicePersonServiceクラスのインスタンスを作成し、そのpersonDaoフィールドにアクセスする方法は次のとおりです。
GuicePersonService personService = injector.getInstance(GuicePersonService.class);
assertNotNull(personService);
assertNotNull(personService.getPersonDao());
4.5. 春のフィールドインジェクション
すべての例で、SpringとGuiceの両方にフィールドインジェクションを適用する方法をすでに見てきました。 ですから、それは私たちにとって新しい概念ではありません。 ただし、完全を期すために、もう一度リストしてみましょう。
フィールドベースの依存性注入の場合、@Autowiredまたは@Injectでマークを付けて依存性を注入します。
4.6. Guiceでのフィールドインジェクション
上記のセクションで説明したように、@Injectを使用したGuiceのフィールドインジェクションについてはすでに説明しました。
5. 結論
このチュートリアルでは、依存性注入を実装する方法において、GuiceフレームワークとSpringフレームワークのいくつかの主要な違いを調査しました。 いつものように、GuiceとSpringのコードサンプルはGitHubにあります。