Spring Framework や Spring Boot を学んでいると、DI という言葉がよく出てきます。
しかし、新人SEの方にとっては、それが実際どういうものなのかを把握しづらい用語なのではないかと思います。
そこで本記事では、DI(依存性の注入)とは何かを、できるだけシンプルに説明します。
細かいフレームワークの話に入る前に、まずはプレーンな Java のコードでイメージをつかむことを目的にします。
なお、本記事ではわかりやすさを重視し、厳密な説明や細かいバリエーションの話はいったん脇に置いています。
DI(依存性の注入)とは
ひとことで言えば、DI とは「そのクラスが使う部品を、そのクラス自身で new せず、外部から受け取って使うこと」です。
もう少しかみ砕くと、あるクラスが別のクラスを利用したいときに、
- 利用される側のクラスを、利用する側のクラスの中で直接生成しない
- 代わりに、外部で生成されたオブジェクトを受け取って利用する
という形にする考え方です。
Spring ではこの仕組みが非常に重要ですが、まずはフレームワークをいったん離れて、「普通の Java コードでどういう違いがあるのか」を見た方が理解しやすいです。
まずは DI ではないコード
最初に、DI を使っていないコードを見てみます。
public class Main {
public static void main(String[] args) {
Service service = new Service();
service.execute();
}
}
public class Service {
public void execute() {
Helper helper = new Helper(); // ここで直接 new している
helper.doWork();
}
}
public class Helper {
public void doWork() {
// 略
}
}
このコードでは、Service クラスの中で Helper を直接 new しています。
つまり Service は、「Helper を使う」という役割だけでなく、「Helper を生成する」という役割まで自分で持っている状態です。
このような書き方自体が間違いというわけではありません。
ただし、クラス同士の結び付きが強くなりやすく、あとで差し替えやテストをしたくなったときに扱いづらくなることがあります。
DI を使ったコード
次に、DI を使った形に書き換えたコードを見てみます。
public class Main {
public static void main(String[] args) {
Helper helper = new Helper(); // ここで生成する
Service service = new Service(helper); // 生成済みの部品を渡す
service.execute();
}
}
public class Service {
private final Helper helper;
public Service(Helper helper) {
this.helper = helper;
}
public void execute() {
helper.doWork(); // 渡された部品を利用する
}
}
public class Helper {
public void doWork() {
// 略
}
}
このコードでは、Helper を new しているのは Service クラスではなく、Main 側です。
そして、生成した Helper をコンストラクタ経由で Service に渡しています。
これが DI の基本的な形です。
Service は、Helper を必要とはしていますが、「どうやって作るか」は知りません。
あくまで「渡されたものを使う」という責務だけを持っています。
何が違うのか
DI を使っていないコードと DI を使ったコードの違いは、単に new する場所が変わっただけのようにも見えるかもしれません。
しかし、実際にはこの違いがかなり重要です。
DI を使わない場合、Service は Helper の具体的な生成方法に強く依存します。
一方で DI を使う場合、Service は「必要な部品を受け取って使う」という形になるため、部品の差し替えがしやすくなります。
たとえば、テスト用の処理に差し替えたり、実装を別のクラスに入れ替えたりしやすくなります。
そのため、DI には次のようなメリットがあります。
- テストしやすくなる
- 実装の差し替えがしやすくなる
- クラスの責務を分けやすくなる
最初は「少し回りくどい」と感じるかもしれませんが、実務のようにクラス数が増え、処理が複雑になってくると、この分離のありがたみが見えてきます。
Spring では何をしてくれるのか
ここまでの例はプレーンな Java で示したものであり、Helper の生成や Service への受け渡しを Main に直接書いています。
Spring Framework では、この「部品を生成して、必要なクラスに渡す」という処理を、フレームワーク側が管理してくれます。
つまり Spring で DI が出てきたときは、
- アプリケーションで使う部品(オブジェクト)を Spring が管理し
- 必要な場所へ自動的に注入してくれる
というイメージで考えると分かりやすいです。
もちろん実際には、Bean 定義、コンポーネントスキャン、コンストラクタインジェクションなど、もう少し具体的な仕組みがあります。
ただ、最初の段階では「クラスの中で何でも new するのではなく、外で用意された部品を受け取って使う考え方」と押さえておけば十分だと思います。
まとめ
DI(依存性の注入)とは、「そのクラスが必要とするオブジェクトを、そのクラスの中で直接生成せず、外部から渡して使う」という考え方です。
Java 初学者のうちは、どうしても「使いたいならその場で new すればよいのでは」と思いやすいです。
実際、小さなサンプルではそれでも動きます。
しかし、実務のアプリケーションでは、部品の差し替え、テストのしやすさ、責務の分離がとても重要になります。
そのため、Spring を学ぶ際にも、DI は単なる文法ではなく、「クラス同士の結び付きを弱めて保守しやすくするための考え方」だと理解すると、かなり腑に落ちやすくなるはずです。