はじめに
Javaと言えばSpringBootと言っても過言ではないほど、有名なフレームワークという認識です。筆者は約3年SpringBootを使用していましたが、エンジニア人生初の開発だったこともあり、DI(依存注入性)について曖昧な点が多かった為、改めて初心者向けに紹介します。
1. DI(依存性の注入)とは?
DIは「dependency injection」の略で、日本語だと依存性注入と分かりにくいと思います。あるクラスの中で使う別のクラス(部品)をnewを使って自分で作成するのではなく、外部から入れてもらう(注入する)という設計の仕組みです。これを「ゲーム機とコントローラー」の関係で例えてみます。
❌ DIなし(本体内蔵型):
ゲーム機本体の中にコントローラーが直接組み込まれていて、絶対に取り外せない状態です。ニンテンドーDSがこれに当たります。
⭕ DIあり(外部接続型):
ゲーム機本体とコントローラーが独立しており、外部からカチャッとコントローラーを接続(注入)して遊ぶ状態です。こちらはNintendo Switchがまさにそうですね。
DIの考え方を取り入れると、ゲーム機本体(クラス)は「どんなコントローラーが繋がっているか」を気にすることなく、ゲームを動かすという本来の役割に集中することができます。
2. なぜDIが必要なのか?(DIのメリット)
最大の理由は変更に強くなること、テストがしやすくなること。この2点です。
もしDIを使わず、ゲーム機の中で特定のコントローラーをnewしてしまうと、そのコントローラーしか使えなくなってしまいます。これを密結合と言い、DSはコントローラーが内蔵されているので、外部のコントローラーは使えないといったイメージです。
しかしDIを使って外部から接続できるようにしておけば、コードを大きく書き換えることなく簡単にパーツを付け替えることができます。これを疎結合と言い、Switchだと標準のジョイコンから操作性の高いプロコンへと切り替えるといったイメージとなります。
また、開発時にテスト用の自動操作コントローラー(モック)を注入することも簡単になるため、プログラムの動作テスト(JUnitやMockitoなど)が劇的に楽になります。
3. Spring BootでのDIのやり方
Spring Bootには「DIコンテナ」という、アプリケーション全体で部品(Bean)を管理し、必要なところに自動で繋いでくれる便利な仕組みがあります。
今回は、現在のSpringにおいて最も推奨されているコンストラクタインジェクションを使った実装を見てみましょう。
① 注入される側(使われるコントローラー)を作る
クラスに @Component などのアノテーションをつけます。これで「DIコンテナに登録して!」とSpringに指示を出します。
import org.springframework.stereotype.Component;
// 共通のインターフェースを用意しておくと付け替えがさらに簡単になります
public interface Controller {
String pushButton();
}
@Component // Springに管理させる部品としてDIコンテナに登録
public class StandardController implements Controller {
@Override
public String pushButton() {
return "ボタンが押されました!キャラクターがジャンプします。";
}
}
② 注入する側(ゲーム機本体)を作る
使う側のクラスでは、コンストラクタを通して部品を受け取ります。自分で new StandardController() をしなくても、Springが起動時に自動で注入してくれます。
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GameConsole {
private final Controller controller;
// コンストラクタインジェクション(現在の推奨)
// ※Spring 4.3以降、コンストラクタが1つの場合は @Autowired アノテーションを省略できる
public GameConsole(Controller controller) {
this.controller = controller;
}
@GetMapping("/play")
public String play() {
// 自分で new せずに、外部から注入されたコントローラーのメソッドを使う
return controller.pushButton();
}
}
【重要】フィールドインジェクションよりコンストラクタを推奨
少し前のネット記事や書籍では、フィールドに直接 @Autowired を付ける「フィールドインジェクション」がよく紹介されていました。しかし、現在では以下の理由からコンストラクタインジェクションが公式で強く推奨されています。
-
final修飾子が使えるため、途中で部品が書き換えられる心配がない(安全性が高い) -
SpringのDIコンテナを立ち上げなくてもテストコードが書ける
これから学習する方は、ぜひコンストラクタインジェクションを使うクセをつけてみてください!
まとめ
-
DIは、必要な部品を自分で
newせず、外部からカチャッと接続してもらう仕組みのこと -
メリットは、コントローラーを付け替えるように部品の変更ができ(疎結合)、テストが圧倒的にしやすくなること
-
Spring Bootでは、@Component 等で部品を登録し、コンストラクタを使って注入するのが現在のベストプラクティス