0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Spring入門】第5章 DI とアプリの構成

0
Last updated at Posted at 2026-09-29

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 コンテナは、アプリの起動時に次のことを自動で行います。

  1. クラスを探す:@Component や @Controller などのアノテーションが付いたクラスを探す
  2. オブジェクトを作って保管する:見つけたクラスのオブジェクトを1つずつ作り、コンテナの中に保管する
  3. 必要な所へ届ける:あるクラスが別のクラスを必要としていたら、保管しているオブジェクトを渡す(注入する)

DI コンテナが作って管理しているオブジェクトのことを Bean(ビーン)といいます。

ch05-1.png

図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 になったりします。置き場所の間違いと症状は、付録B「ファイルの置き場所」 にまとめています。

5.3 やってみよう:部品を DI で受け取る

第4章で作った HelloController を使って、実際に DI を体験しましょう。メッセージを返す部品 GreetingMessage を作り、HelloController で new を使わずに受け取ります。

手順1:GreetingMessage クラスを作る

  1. Eclipse の パッケージ・エクスプローラー で、employee-app プロジェクトの src/main/java を開き、com.example.employeeapp パッケージを右クリックします。

  2. 新規 → クラス を選びます。

    new-class-menu.png
    図5-2 com.example.employeeapp パッケージを右クリック → 新規 → クラス

    【再確認】クラスは、置きたいパッケージを右クリックして作る

    「新規 → クラス」は、必ず クラスを置くパッケージ を右クリックしてから選びます。右クリックしたパッケージが、次の画面の「パッケージ」欄に自動で入るので、入力の手間と間違いが減ります。置きたいパッケージがまだないときは、今回のように その親のパッケージ を右クリックし、「パッケージ」欄の末尾に書き足します。

  3. 「新規 Java クラス」画面で、次のように入力します。 パッケージ欄にはあらかじめ com.example.employeeapp が入っているので、末尾に .component を書き足します。存在しないパッケージを指定すると、クラスの作成と同時にパッケージも作られます。

    項目 入力する値
    パッケージ com.example.employeeapp.component
    名前 GreetingMessage

new-class-dialog.png
図5-3 新規 Java クラス(パッケージの末尾に .component を書き足し、名前を入力して「完了」)

  1. 完了 ボタンを押します。
  2. エディターに開いた GreetingMessage.java を、次のように書きます。
src/main/java/com/example/employeeapp/component/GreetingMessage.java
package com.example.employeeapp.component;

import org.springframework.stereotype.Component;

@Component
public class GreetingMessage {

    public String getMessage() {
        return "DI で受け取ったメッセージです";
    }
}

greeting-message.png
図5-4 GreetingMessage.java(保存するとタブの「*」が消える)

ポイントは、クラスの上に付けた @Component です。これは「このクラスを Bean として DI コンテナで管理してください」という目印です。

【再確認】@Component も補完で入力する

@Comp まで打って Ctrl + Space を押し、候補から Component - org.springframework.stereotype を選びます。補完で選ぶと import 文も自動で追加されます(第4章「入力は補完で行う」)。全部打ち込んでしまった場合は、語の末尾で Ctrl + Space を押して選び直してください。

手順2:HelloController を修正する

  1. パッケージ・エクスプローラーで com.example.employeeapp.controller パッケージの HelloController.java をダブルクリックして開きます。
  2. 次のように修正します。色の付いた行が、追加・変更する部分です。
src/main/java/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";
    }
}

hello-controller-di.png
図5-5 修正した HelloController.java(赤枠が @Autowired で受け取る部分)

@Autowired の行に黄色い波線が出ますが、エラーではありません。Spring が推奨する書き方が変わったため、書き方を変換する機能があることを知らせています。この教材では、現場で多く書かれているこの書き方のまま進めます。

【知識メモ】Spring が推奨する書き方:コンストラクタ注入

この黄色い波線は、Spring が、フィールドに @Autowired を付けて受け取る今の書き方(フィールド注入。5.4 で説明します)よりも、コンストラクタ注入 を推奨していることを知らせるものです。コンストラクタ注入では、使う部品をコンストラクタの引数で受け取ります。

HelloController.java(コンストラクタ注入で書いた例。この教材では使いません)
@Controller
public class HelloController {

    private final GreetingMessage greetingMessage;

    // DI コンテナがこのコンストラクタを呼び、GreetingMessage の Bean を渡す
    public HelloController(GreetingMessage greetingMessage) {
        this.greetingMessage = greetingMessage;
    }

    ...
}

コンストラクタを呼んで GreetingMessage の Bean を渡すのは DI コンテナです。自分で new を書かないことは、フィールド注入と同じです。コンストラクタが1つだけのクラスでは、@Autowired を付けなくても、DI コンテナがそのコンストラクタを使います。

コンストラクタ注入が推奨される理由は、次の3つです。

  • フィールドに final を付けられるので、受け取った部品が後から書き換えられない
  • 部品が足りないときに、アプリの起動時にエラーになって気付ける
  • テストのときに、コンストラクタの引数で部品を自分で渡せる

Spring の公式ドキュメント(英語)の「Dependency Injection」のページでも、必ず使う部品はコンストラクタ注入で受け取るよう勧めています。

この教材は、現場の既存のコードで多く見かけるフィールド注入で進めます。どちらで書いても、DI コンテナが部品を届けてくれる仕組みは同じです。新しく作るプロジェクトでは、チームの決まりに合わせてください。Service 層のクラスをコンストラクタ注入で書いた例は、第15章「コンストラクタ注入」で紹介します。

ここで注目してほしいのは、どこにも new GreetingMessage() と書いていない ことです。HelloController は、フィールドに @Autowired を付けて「GreetingMessage が必要です」と伝えているだけです。

手順3:動作を確認する

  1. Ctrl + S で2つのファイルを保存します(エディターのタブの「*」が消えれば保存できています)。アプリを起動している場合は、コンソールの赤い四角(終了)ボタンで一度停止してから、プロジェクトを右クリック → 実行 → Spring Boot アプリケーション で起動し直します(Boot ダッシュボードの再起動ボタンでもかまいません)。停止している場合は、そのまま起動します。保存しただけでは、動いているアプリには反映されません。
  2. コンソールに Started EmployeeAppApplication と表示されたら、Chrome で http://localhost:8080/hello を開きます。

chrome-hello-di.png
図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 コンテナが次のように動いているからです。

  1. コンポーネントスキャンで、@Component が付いた GreetingMessage と、@Controller が付いた HelloController を見つける
  2. GreetingMessage のオブジェクト(Bean)を作る
  3. HelloController のオブジェクト(Bean)を作る
  4. HelloController の @Autowired が付いたフィールドに、2で作った GreetingMessage の Bean を代入する

つまり、new はなくなったわけではなく、DI コンテナが代わりに実行してくれている のです。@Autowired は「このフィールドに Bean を入れてください」という目印です。このように、フィールドに部品を入れてもらう方法を フィールド注入 といいます。部品をコンストラクタの引数で受け取る コンストラクタ注入 という方法もあり、Spring はそちらを推奨しています(5.3 の「Spring が推奨する書き方」の囲み)。

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層構成 で作ります。

パッケージ構成

クラスは役割ごとにパッケージを分けて置きます。今の時点では、パッケージ・エクスプローラーは次のようになっています。

explorer-packages.png
図5-7 第5章の終わりのパッケージ構成(component と controller)

完成時には、次のようなパッケージ構成になります。

src/main/java(完成時)
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 とまったく同じ形で受け取ります。

予告:第8章で作る EmployeeListController(一部)
@Controller
public class EmployeeListController {

    @Autowired
    private EmployeeRepository employeeRepository;

    ...
}

ch05-2.png

図5-8 社員一覧の Controller は Repository を DI で受け取る

EmployeeRepository の中身は、第7章で作ります。

【知識メモ】MyBatis コースの場合

MyBatis コースでは、repository パッケージの代わりに mapper パッケージを作り、データベースを操作する部品 EmployeeMapper・DepartmentMapper(第7章 MyBatis 入門②)を置きます。Mapper には @Repository の代わりに、MyBatis の @Mapper を付けます。entity パッケージの Employee・Department は、名前も役割も JPA コースと同じです(第6章 MyBatis 入門①)。

Controller は、Repository の代わりに Mapper を、同じ形で DI で受け取ります。

予告:第8章で作る EmployeeListController(MyBatis コース。一部)
@Controller
public class EmployeeListController {

    @Autowired
    private EmployeeMapper employeeMapper;

    ...
}

Mapper のメソッドの名前は JPA コースの Repository と同じにしてあるので、第8章以降のコードは、EmployeeRepository を EmployeeMapper に、employeeRepository を employeeMapper に読み替えると同じ形になります。ほかにも違うところには、各章に「MyBatis コースの場合」の囲みを置いています。

【知識メモ】練習用のクラスについて

HelloController と GreetingMessage は練習用なので、社員管理アプリの機能には使いません。削除せずにそのまま残しておいてかまいません。

5.7 第6章からのコースを選ぶ

第6章と第7章では、データベースを操作する仕組みを学びます。この教材では、JPA コースと MyBatis コースのどちらかを選びます。第8章以降は、どちらを選んでも同じ内容です。

JPA は「Java のオブジェクトとテーブルを対応させる方法」を決めた仕様(決まりごと)で、実際に SQL を作って動かすのは、仕様を実装したライブラリです。実装はいくつかあり、Spring Boot では標準で Hibernate(ハイバネート)が使われます。

JPA(Spring Data JPA) MyBatis
SQL 自分では書かない。メソッド名などから Hibernate が自動で作る 自分で書く(XML ファイル)
書く量 少ない SQL の分だけ多い
向いている場面 新しく作るシステム、単純な操作が多いシステム 複雑な SQL が多いシステム、SQL を細かく管理したい現場
学ぶときの負担 SQL を書かずに始められる SQL の知識が要る

選び方の目安は次のとおりです。

  • 配属先や参加するプロジェクトで使う仕組みが決まっているなら、そちらを選びます。
  • 業務で SQL を書いたことがあり、SQL でデータを扱いたいなら、MyBatis コースが向いています。
  • どちらか分からない・迷うときは、JPA コースを選んでください。初めて学ぶ人が始めやすく、この教材の標準のコースです。

MyBatis コースは、第4章の「MyBatis コースで進める場合」のとおりに、MyBatis を使う設定でプロジェクトを作ってあることが前提です。

5.8 まとめと確認問題

まとめ

  • DI(依存性の注入)は、使う部品を自分で new せず、外から届けてもらう考え方である
  • Spring の DI コンテナ は、起動時にアノテーションの付いたクラスを探し、Bean を作って保管し、必要な所へ注入する
  • DI コンテナが探す範囲(コンポーネントスキャン)は、@SpringBootApplication が付いたクラスのパッケージとその下である
  • 部品を受け取るフィールドには @Autowired を付ける。DI コンテナが Bean を代入してくれる(フィールド注入)。Spring が推奨するのはコンストラクタの引数で受け取る コンストラクタ注入 だが、この教材は現場で多いフィールド注入で統一する
  • @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 になります。


前へ

第4章 最初の Spring Boot アプリ

次へ

第6章 JPA 入門① Entity(JPA コース)

第6章 MyBatis 入門① データのクラス(MyBatis コース)

目次へ

【Spring入門】Webアプリを作りながら学ぶ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?