はじめに
ここでのDIとは「Dependency Injection(依存性の注入)」のDIです。
似てるもので「Dependency Inversion Principle(依存性逆転の原則)」とありますがこれはDIPですね。
ずいぶん前ですが自分は同じものだけど呼び方が違うだけだと思ってて統一してくれよーとか思ってました。
話を戻します。
DIとは凄く簡単に説明しますと「依存関係を直接インスタンス化(つまりnewを記述)するのではなく、外部から依存関係を渡す設計パターン」です。
以下のように引数として外部のインスタンスを渡すことも依存性注入の一形態らしいです。
public void process(SampleProvider sampleProvider) {
// なんらかの処理ロジック
}
このように外部から引数として渡すことで若干疎結合に貢献することが出来ますが
どこかしらでnew SampleProvider()みたいにnewを記述する必要が出てくるため結局newが書かれた場所はSampleProviderと密結合になってしまいます。
所がどっこいSpringだとそのnewの記述を完全に抹消することが可能です。別にSpringだけじゃなく多言語の主要FWのほとんどが何らかのDIの仕組みを持っていることのほうが多いと思います。
そしてSpringではDIを実現する際に以下3つの方法で依存性を注入することが出来ます。
- フィールドインジェクション
- コンストラクタインジェクション
- メソッドインジェクション
これらそれぞれの特徴をchatGPTに聞いてみましたが納得です。
上記メリデメを理解した上で個人的な検討順序としてはコンストラクタ > フィールド > メソッド かなーと思いますので今回はコンストラクタインジェクションのやり方を簡単に以下に記載します。
コンストラクタインジェクション
①Beanの登録
まずはコンストラクタインジェクションに関わらずDIしたいクラスをApplication Contextというコンテナに対して登録する必要があり、Application Contextで登録されたDI対象クラスのことをBeanと呼びます。
@Service
public class SampleService {
// なんか色々
}
上記のように@Service
と付けるだけでBeanの登録は完了です。
②Beanの取得
次にSampleServiceを利用するクラス内でSampleServiceのBeanを取得します。
実はコンストラクタインジェクションでも2パターン書き方があるようなので②-1, ②-2で分けて紹介します。
②-1 Autowired記述Ver
以下のようにSampleServiceのフィールドを用意してSampleServiceを引数としたコンストラクタに対し@Autowiredを記述したら完了です。
@Controller
@RequestMapping("/")
public class SampleController {
private SampleService sampleService;
@Autowired
public TaskController(SampleService sampleService) {
this.sampleService = sampleService;
}
...
}
②-2 Autowired記述なしVer
以下のようにSampleServiceのフィールドをfinalで修飾子してSampleServiceを引数としたコンストラクタを用意したら完了です。
@Controller
@RequestMapping("/")
public class SampleController {
private final SampleService sampleService;
// @Autowired ← あってもエラーにはならないがなくても良い。
public TaskController(SampleService sampleService) {
this.sampleService = sampleService;
}
...
}
③所感
めちゃくちゃ簡単ですね。個人的には②-2のようにfinalで修飾しておくほうがより堅牢になって良いなと思いました。ただ、@AutowiredなくてもSpringがよしなに処理してくれるってことを知らなかったのでもっとこのやり方が世の中に根付いてほしいと思いました。(俺が知らなかっただけかも笑)