Spring の心臓部ともいえる「DI(依存性の注入)」の考え方を学びます。あわせて、これから作る社員管理アプリのクラスをどのパッケージに置くのか、全体の構成を確認します。
この章で学ぶこと
- DI とは何か、なぜ必要なのか
- DI コンテナと Bean の関係
- @Component を付けたクラスを、@Autowired で受け取る方法
- Spring の主なアノテーションの役割
- 社員管理アプリのパッケージ構成(Controller と Repository の2層構成)
5.1 なぜ DI が必要なのか
プログラムは、たくさんのクラス(部品)が協力し合って動きます。あるクラスが別のクラスを使うとき、使われる側のクラスを「依存しているもの」といいます。たとえば HelloController がメッセージを作るクラスを使うなら、HelloController はそのクラスに 依存している ことになります。
これまでの書き方:自分で new する
Java の学習では、使いたいクラスがあれば new でオブジェクトを作ってきました。第4章の HelloController で、メッセージを作る GreetingMessage クラスを使うとしたら、次のように書くでしょう。
@GetMapping("/hello")
public String hello(Model model) {
// 使う部品を、自分で new して作る
GreetingMessage greetingMessage = new GreetingMessage();
model.addAttribute("message", greetingMessage.getMessage());
return "hello";
}
このプログラムは正しく動きます。しかし、アプリが大きくなると、次のような問題が出てきます。
| 問題 | 具体的には |
|---|---|
| 部品の差し替えが難しい | GreetingMessage を別のクラスに取り替えたくなったら、new GreetingMessage() と書いたすべての場所を探して直す必要がある |
| 作り方があちこちに散らばる | 部品を作るのに準備(設定値の読み込みやデータベースへの接続など)が必要な場合、その準備のコードを使う側のすべてのクラスに書くことになる |
| 作る順番を管理しなければならない | ある部品が別の部品を使う場合、使われる部品を先に new して渡す必要がある。部品が増えるほど、どれを先に作るかの管理が大変になる |
| テストしにくい | クラスの中で部品を作ってしまうと、テストのときだけ「テスト用の部品」に取り替える、といったことができない |
DI の考え方:部品は外から届けてもらう
そこで、「使う部品は自分で作らず、外から届けてもらう」という考え方が生まれました。これを DI(Dependency Injection:依存性の注入)といいます。「依存しているもの(部品)を、外から注入する(入れてもらう)」という意味です。
DI を使うと、使う側のクラスの中で new GreetingMessage() のように 自分で new を書くことはなくなります。部品のクラスに適切なアノテーション(@Component など)を付けておくと、Spring がそのオブジェクトを内部で new して用意します。
部品を作る仕事を Spring に任せるので、「どの部品を先に作ってから、どれに渡すか」という 作る順番を、自分で考える必要もなくなります。Spring が部品どうしの関係を見て、必要な順番で用意してくれます(5.4 で詳しく見ます)。
Spring では、Controller や Repository など、Spring に用意してもらう部品は、自分で new しないのが原則です。一方、Entity やフォームのように、データを入れて持ち運ぶためのオブジェクトは、これまでどおり自分で new します。
5.2 DI コンテナと Bean
Spring の中で、部品のオブジェクトを用意して届ける役割をするのが DI コンテナ です。DI コンテナは、アプリの起動時に次のことを自動で行います。
-
クラスを探す:
@Componentや@Controllerなどのアノテーションが付いたクラスを探す - オブジェクトを作って保管する:見つけたクラスのオブジェクトを1つずつ作り、コンテナの中に保管する
- 必要な所へ届ける:あるクラスが別のクラスを必要としていたら、保管しているオブジェクトを渡す(注入する)
DI コンテナが作って管理しているオブジェクトのことを Bean(ビーン)といいます。

図5-1 DI コンテナが Bean を作り、必要な所へ注入する
コンポーネントスキャン:どこを探すのか
DI コンテナがクラスを探すことを コンポーネントスキャン といいます。探す範囲は、@SpringBootApplication が付いたクラス(第4章で自動生成された EmployeeAppApplication)が置かれている パッケージと、その下のパッケージすべて です。
com.example.employeeapp ← EmployeeAppApplication がある(ここから下が対象)
├─ com.example.employeeapp.controller ← 対象
├─ com.example.employeeapp.component ← 対象
└─ com.example.employeeapp.repository ← 対象
com.example.other ← 対象外(見つけてもらえない)
【ご注意】パッケージは必ず com.example.employeeapp の下に作る
この教材で作るクラスは、すべて com.example.employeeapp の下のパッケージに置きます。別の場所に置くと、アノテーションを付けていても DI コンテナに見つけてもらえず、起動時にエラーになったり、URL にアクセスしても 404 になったりします。
5.3 やってみよう:部品を DI で受け取る
第4章で作った HelloController を使って、実際に DI を体験しましょう。メッセージを返す部品 GreetingMessage を作り、HelloController で new を使わずに受け取ります。
手順1:GreetingMessage クラスを作る
-
Eclipse の パッケージ・エクスプローラー で、
employee-appプロジェクトのsrc/main/javaを開き、com.example.employeeappパッケージを右クリックします。 -
新規 → クラス を選びます。

図5-2 com.example.employeeapp パッケージを右クリック → 新規 → クラス【再確認】クラスは、置きたいパッケージを右クリックして作る
「新規 → クラス」は、必ず クラスを置くパッケージ を右クリックしてから選びます。右クリックしたパッケージが、次の画面の「パッケージ」欄に自動で入るので、入力の手間と間違いが減ります。置きたいパッケージがまだないときは、今回のように その親のパッケージ を右クリックし、「パッケージ」欄の末尾に書き足します。
-
「新規 Java クラス」画面で、次のように入力します。 パッケージ欄にはあらかじめ
com.example.employeeappが入っているので、末尾に.componentを書き足します。存在しないパッケージを指定すると、クラスの作成と同時にパッケージも作られます。項目 入力する値 パッケージ com.example.employeeapp.component名前 GreetingMessage

図5-3 新規 Java クラス(パッケージの末尾に .component を書き足し、名前を入力して「完了」)
- 完了 ボタンを押します。
- エディターに開いた
GreetingMessage.javaを、次のように書きます。
package com.example.employeeapp.component;
import org.springframework.stereotype.Component;
@Component
public class GreetingMessage {
public String getMessage() {
return "DI で受け取ったメッセージです";
}
}

図5-4 GreetingMessage.java(保存するとタブの「*」が消える)
ポイントは、クラスの上に付けた @Component です。これは「このクラスを Bean として DI コンテナで管理してください」という目印です。
【再確認】@Component も補完で入力する
@Comp まで打って Ctrl + Space を押し、候補から Component - org.springframework.stereotype を選びます。補完で選ぶと import 文も自動で追加されます(第4章「入力は補完で行う」)。全部打ち込んでしまった場合は、語の末尾で Ctrl + Space を押して選び直してください。
手順2:HelloController を修正する
- パッケージ・エクスプローラーで
com.example.employeeapp.controllerパッケージのHelloController.javaをダブルクリックして開きます。 - 次のように修正します。色の付いた行が、追加・変更する部分です。
package com.example.employeeapp.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.example.employeeapp.component.GreetingMessage;
@Controller
public class HelloController {
@Autowired
private GreetingMessage greetingMessage;
@GetMapping("/hello")
public String hello(Model model) {
model.addAttribute("message", greetingMessage.getMessage());
return "hello";
}
}

図5-5 修正した HelloController.java(赤枠が @Autowired で受け取る部分)
@Autowired の行に黄色い波線が出ますが、エラーではありません。Spring が推奨する書き方が変わったため、書き方を変換する機能があることを知らせています。この教材では、現場で多く書かれているこの書き方のまま進めます。
ここで注目してほしいのは、どこにも new GreetingMessage() と書いていない ことです。HelloController は、フィールドに @Autowired を付けて「GreetingMessage が必要です」と伝えているだけです。
手順3:動作を確認する
- Ctrl + S で2つのファイルを保存します(エディターのタブの「*」が消えれば保存できています)。アプリを起動している場合は、コンソールの赤い四角(終了)ボタンで一度停止してから、プロジェクトを右クリック → 実行 → Spring Boot アプリケーション で起動し直します(Boot ダッシュボードの再起動ボタンでもかまいません)。停止している場合は、そのまま起動します。保存しただけでは、動いているアプリには反映されません。
- コンソールに
Started EmployeeAppApplicationと表示されたら、Chrome でhttp://localhost:8080/helloを開きます。

図5-6 GreetingMessage のメッセージが表示される
GreetingMessage が返したメッセージが表示されれば成功です。
【ご注意】うまくいかないときは
-
起動時に「Field greetingMessage in … required a bean of type '…GreetingMessage' that could not be found.」と表示される:GreetingMessage に
@Componentを付け忘れているか、パッケージがcom.example.employeeappの下になっていません。 - メッセージが「Hello, Spring Boot!」のまま:ファイルが保存されていないか、アプリが再起動されていません。保存してから、アプリを停止・起動し直してください。
5.4 @Autowired の解説
なぜ new していないのに null にならないのか
Java の基本では、フィールドに何も代入しなければ null のままで、メソッドを呼ぶと NullPointerException になります。それなのに、今回のコードでは greetingMessage.getMessage() が問題なく動きました。
これは、アプリの起動時に DI コンテナが次のように動いているからです。
- コンポーネントスキャンで、
@Componentが付いた GreetingMessage と、@Controllerが付いた HelloController を見つける - GreetingMessage のオブジェクト(Bean)を作る
- HelloController のオブジェクト(Bean)を作る
- HelloController の
@Autowiredが付いたフィールドに、2で作った GreetingMessage の Bean を代入する
つまり、new はなくなったわけではなく、DI コンテナが代わりに実行してくれている のです。@Autowired は「このフィールドに Bean を入れてください」という目印です。このように、フィールドに部品を入れてもらう方法を フィールド注入 といいます。
5.5 Spring の主なアノテーション
DI コンテナに Bean として管理してもらうためのアノテーションには、役割ごとにいくつかの種類があります。どれも「このクラスを Bean にしてください」という点は同じで、クラスの役割を名前で表している と考えてください。
| アノテーション | 付けるクラス | この教材では |
|---|---|---|
@Component |
汎用的な部品。どの役割にも当てはまらないクラス | この章の GreetingMessage |
@Controller |
リクエストを受け取り、画面を返すクラス | HelloController、社員の一覧・登録・変更の Controller |
@Repository |
データベースを操作するクラス | EmployeeRepository、DepartmentRepository(第7章) |
@Service |
業務の処理をまとめるクラス | 使わない(第15章で紹介) |
@SpringBootApplication |
アプリの起動クラス。コンポーネントスキャンの起点になる | EmployeeAppApplication(自動生成) |
5.6 社員管理アプリの構成
DI の考え方がわかったところで、これから作る社員管理アプリの構成を確認しましょう。第1章で説明したとおり、この教材では Controller が直接 Repository を使う2層構成 で作ります。
パッケージ構成
クラスは役割ごとにパッケージを分けて置きます。今の時点では、パッケージ・エクスプローラーは次のようになっています。

図5-7 第5章の終わりのパッケージ構成(component と controller)
完成時には、次のようなパッケージ構成になります。
com.example.employeeapp
├─ EmployeeAppApplication.java 起動クラス(自動生成)
├─ controller
│ ├─ HelloController.java 練習用(第4・5章)
│ ├─ EmployeeListController.java 一覧・削除(第8・9章)
│ ├─ EmployeeRegisterController.java 新規登録(第10・11・13章)
│ └─ EmployeeUpdateController.java 変更(第12・14章)
├─ repository
│ ├─ EmployeeRepository.java 社員テーブルの操作(第7章)
│ └─ DepartmentRepository.java 部署テーブルの操作(第7章)
├─ entity
│ ├─ Employee.java 社員テーブルの1行(第6章)
│ └─ Department.java 部署テーブルの1行(第6章)
├─ form
│ ├─ RegisterForm.java 新規登録の入力画面の値を受け取る(第10章)
│ └─ UpdateForm.java 変更の入力画面の値を受け取る(第12章)
└─ component
└─ GreetingMessage.java 練習用(第5章)
| パッケージ | 置くクラス | 役割 | 作る章 |
|---|---|---|---|
| controller | EmployeeListController EmployeeRegisterController EmployeeUpdateController |
URL ごとの処理を書く。Repository を使ってデータを取り出し、画面に渡す | 第8章〜 |
| repository | EmployeeRepository DepartmentRepository |
データベースへの検索・登録・変更・削除を担当する | 第7章 |
| entity | Employee Department |
テーブルの1行分のデータを表す | 第6章 |
| form | RegisterForm UpdateForm |
入力画面に入力された値を受け取る | 第10章・第12章 |
| component | GreetingMessage | この章の練習用 | 第5章 |
Controller は Repository を DI で受け取る
この章で学んだ DI は、これからの章で何度も使います。たとえば第8章で作る社員一覧の Controller は、社員テーブルを操作する EmployeeRepository を、GreetingMessage とまったく同じ形で受け取ります。
@Controller
public class EmployeeListController {
@Autowired
private EmployeeRepository employeeRepository;
...
}

図5-8 社員一覧の Controller は Repository を DI で受け取る
EmployeeRepository の中身は、第7章で作ります。
【知識メモ】練習用のクラスについて
HelloController と GreetingMessage は練習用なので、社員管理アプリの機能には使いません。削除せずにそのまま残しておいてかまいません。
5.7 まとめと確認問題
まとめ
-
DI(依存性の注入)は、使う部品を自分で
newせず、外から届けてもらう考え方である - Spring の DI コンテナ は、起動時にアノテーションの付いたクラスを探し、Bean を作って保管し、必要な所へ注入する
- DI コンテナが探す範囲(コンポーネントスキャン)は、
@SpringBootApplicationが付いたクラスのパッケージとその下である - 部品を受け取るフィールドには
@Autowiredを付ける。DI コンテナが Bean を代入してくれる(フィールド注入) -
@Component・@Controller・@Repositoryは、どれも Bean にするための目印で、クラスの役割を表している - 社員管理アプリは controller / repository / entity / form のパッケージに分けて作り、Controller は Repository を DI で受け取る
確認問題
問1 DI(Dependency Injection)とはどのような考え方ですか。簡単に説明してください。
解答を見る
クラスが使う部品(依存しているもの)を自分で new して作るのではなく、外から渡してもらう(注入してもらう)考え方です。Spring では DI コンテナが部品を作って渡してくれます。
問2 DI コンテナが作って管理しているオブジェクトのことを何と呼びますか。
解答を見る
Bean(ビーン)です。
問3 com.example.other パッケージに @Component を付けたクラスを作りましたが、HelloController に注入しようとすると起動時にエラーになりました。原因は何ですか。
解答を見る
コンポーネントスキャンの範囲外だからです。DI コンテナが探すのは @SpringBootApplication が付いた EmployeeAppApplication のパッケージ(com.example.employeeapp)とその下だけなので、com.example.other のクラスは見つけてもらえません。
問4 次のコードで、greetingMessage に何も代入していないのに、null にならないのはなぜですか。
@Autowired
private GreetingMessage greetingMessage;
解答を見る
@Autowired が付いているので、アプリの起動時に DI コンテナが GreetingMessage の Bean を、このフィールドに代入してくれるからです。
問5 問4 のコードで @Autowired を付け忘れると、どうなりますか。
解答を見る
DI コンテナがフィールドに Bean を代入しないので、greetingMessage は null のままです。greetingMessage.getMessage() を呼んだときに NullPointerException になります。