はじめに
本記事ではSpringの@Autowiredを使用したDI(インジェクション)の種類・使い分けについてまとめます。
DIとは
DIとは「Dependency Injection(依存性の注入)」の略称であり、オブジェクト指向プログラミングにおける開発手法の1つです。
この言葉だけ聞いてもピンとこないと思うので、具体的に言うと「あるクラスに必要となるまた別のクラスのインスタンスを設定すること」です。
DIをする方法
1.特定のアノテーション(@Component,@Controller,@Service等々)をクラスに付与することで、DIコンテナに登録。
2.インスタンスを「@Autowired」によって、他のクラスの変数や引数に代入して使用することができる。
引用:https://learning-collection.com/springboot入門-vol-8:diを理解しよう/
DI(インジェクション)の種類
Springで@AutowiredによるDIには以下3種類があります。
・セッターインジェクション(使い道なし)
・フィールドインジェクション(非推奨)
・コンストラクタインジェクション(推奨)
セッターインジェクション(使い道なし)
こちらはsetterを利用してDIをする方法です。
setterを使うという性質上、コンストラクタ呼び出し後に書き換えが可能なので、あまり使われないです。(セッターインジェクション使うならコンストラクタインジェクション使うかなという感じ)
public class WcupClass{
private final MessiService messiService;
@Autowired
public setMessiService(MessiService messiService){
this.messhiService = messiService;
}
// 以下省略
}
- メリット
- テスト時にモックオブジェクトに置き換えやすい
- デメリット
- 依存するコンポーネントが増えるにつれて引数が多くなる
- フィールドをfinalにできない(書き換えが発生する可能性がある)
フィールドインジェクション(非推奨)
一番よく見る書き方。簡単にかけるので使いがちです。なぜ非推奨なのかは、コンストラクタインジェクションのメリット・デメリットの説明で理解できると思います。
public class WcupClass{
@Autowired
private MessiService messiService;
// 以下省略
}
- メリット
- コンストラクタ、セッターは不要なので、コード量を少なくすることができる。
- デメリット
- DIコンテナを利用することが前提となる。(テスト等で使用すると毎回コンテナ起動が入るので重い)
- フィールドをfinal化できない(書き換えが発生する可能性がある)
コンストラクタインジェクション(推奨)
こちらはコンストラクタを利用してDIする方法です。最も推奨されるDIの方法です。
作られるのがインスタンスを生成するタイミングで呼び出されるため、他2つと違ってfinalで定義することができます。(書き換えが起こらない)
プラス実は以下記載のデメリットは実はメリットです。別途以下にて説明します。
public class WcupClass{
private final MessiService messiService;
@Autowired
privete WcupClass(MessiService messiService) {
this.messiService = messiService;
}
// 以下省略
}
- メリット
- フィールドをfinal化できる
- テスト時にモックオブジェクトに置き換えやすい(SpringBootTestいらないので軽い)
- デメリット
- 依存するコンポーネントが増えるにつれて引数が多くなる
- 循環依存の場合は利用できない
コンストラクタインジェクションのデメリットは実はメリット?
- デメリット1:依存するコンポーネントが増えるにつれて引数が多くなる
- 依存関係が多い場合、そのクラスは多くの責任を持ち過ぎている可能性があります。 SOLID原則の1つの単一責任の原則に反します。
- まとめるとコンストラクタインジェクションは単一責任の原則に反している可能性があることに気づかせてくれるということです。
- デメリット2:循環依存の場合は利用できない
- コンストラクインジェクションは循環依存の場合は利用できないです。ただ循環依存はアンチパターンとされているので、クラス設計が間違えている可能性に気づかせてくれます。
- <参考>
- 循環依存とは・・・AクラスがBクラスをインジェクト、BクラスがCクラスをインジェクト、CクラスがAクラスをインジェクトするような場合
Lombokを使用したコンストラクタインジェクションの書き方
Spring 4.3からクラス中のコンストラクタが1つの場合は@Autowiredを省略できるようになりました。
またLombokの@RequiredArgsConstructorアノテーションを組み合わせることでコンストラクタを記述することなくコンストラクタインジェクションを行うことができます。
@RequiredArgsConstructor
public class WcupClass{
private final MessiService messiService;
// 以下省略
}
終わり
以上で今後はコンストラクタインジェクションで書くのを推奨します!
参考
https://sebenkyo.com/2020/07/18/post-1178/
https://pppurple.hatenablog.com/entry/2016/12/29/233141
https://qiita.com/kmuro/items/aead50c699fefe56c120