お問い合わせ

【改訂版】第2章 DIの基本編|誰もが一度はつまずくSpring Bootを解説

このシリーズでは、Spring Bootで誰もが一度はつまずきやすいポイントを、Javaの経験が浅い方でも無理なく理解できるように、できるだけ難しい言葉を使わずに解説していきます。

今回は第2章「DIの基本編」です。Spring Bootで開発するうえで必ず押さえておきたい「依存性注入(DI)」の仕組みを、イメージで理解していきましょう。

DIとは?

まず「DI」という言葉の意味から確認していきましょう。

DIとは、Dependency Injection の頭文字を取った言葉です。「Dependency」は「依存性」、「Injection」は「注入」を意味します。そのため、日本語では「依存性注入」と呼ばれています。

ただ、「依存性を注入する」と聞いても、まだイメージしにくいのではないでしょうか。

そこで次は、コントローラクラスとサービスクラスを例に、DIの前提となる「依存関係」について見ていきましょう。

依存関係のイメージ

コントローラクラスとサービスクラスの関係を見てみましょう。

図の見方は次のとおりです。

  • 左側にはコントローラクラスの MainController があります
  • 右側にはサービスクラスの MainService があります
  • MainControllermainMethod では、MainServiceserviceMethod を呼び出しています

つまり、MainControllerMainService がなければ必要な処理を完了できません。

このように、あるクラスが別のクラスを必要としている状態を「依存関係」と呼びます。今回の例では、MainControllerMainService に依存しています。

それでは、Springは MainService をどのように準備するのでしょうか。次で見ていきましょう。

インスタンス化と内部メモリへの格納

続いて、インスタンス化と、コンテナへの格納について見ていきます。

Spring Bootが起動すると、Springフレームワークは MainControllerMainService をインスタンス化します。インスタンス化とは、クラスから実際に使用できるオブジェクトを生成することです。

そして、生成した2つのインスタンスを、内部メモリ上にあるコンテナへ格納します。このコンテナに格納されたインスタンスは、Springによって管理されます。

これで、依存性注入を行うための準備が整いました。

内部メモリから依存性注入まで

コンテナには、MainControllerMainService の2つのインスタンスが格納されています。

次にSpringは、MainControllerMainService を必要としていることを確認します。依存関係が確認できると、コンテナで管理している MainServiceMainController へ注入します。

これによって、MainControllermainMethod から、MainService の処理を呼び出せるようになります。

このように、Springが必要なクラスを注入する仕組みを「DI」、日本語では「依存性注入」と呼びます

DIされるまでの流れ

ここまでの流れを整理します。

  1. インスタンス化:Spring Bootの起動直後に、対象となるクラスをインスタンス化する
  2. 内部メモリへ格納:生成したインスタンスを、内部メモリ上のコンテナへ格納する
  3. 依存関係を確認:どのクラスが、どのクラスを必要としているのかをSpringが確認する
  4. 注入(DI):必要なクラスを、依存しているクラスへ注入する

この4つの流れによって、Springの依存性注入が行われます。

次は、この仕組みが実際のコードでどのように表現されるのかを見ていきましょう。

DIのコード例

依存性注入(DI)のコード例を見ていきましょう。

まず、コントローラクラスの MainController です。mainMethod では、MainServiceserviceMethod を呼び出しています。

@RestController
class MainController {

  @Autowired
  MainService mainService;

  @GetMapping("/main")
  String mainMethod() {
    return mainService.serviceMethod();
  }
}

次に、サービスクラスの MainService です。serviceMethod は「Hello World」という文字列を返します。

@Service
class MainService {

  String serviceMethod() {
    return "Hello World";
  }
}

それでは、この2つのクラスでDIを行うためのアノテーションを見ていきましょう。

  • MainController には @RestController を、MainService には @Service を付けています。これらのアノテーションは、対象のクラスをSpringに認識してもらうための目印です。Springはこの目印が付いたクラスをインスタンス化し、コンテナに格納します
  • MainController では、MainService のフィールドに @Autowired を付けています。@Autowired は、コンテナに格納されている MainService をここへ注入してほしいという指示です

これによって、Springが MainServiceMainController へ注入します。MainService が注入されると、mainMethod から serviceMethod を呼び出せるようになります。

このように、@RestController@Service を目印にSpringがコンテナへ格納し、@Autowired で必要なクラスを注入します。

コンテナへの格納に使う主なアノテーション

最後に、Springのコンテナへの格納に使われる、主なアノテーションを紹介します。

アノテーション使い方
@Controller / @RestControllerクラスに付ける
@Serviceクラスに付ける
@Repositoryクラスに付ける
@Componentクラスに付ける
@Configuration + @Bean設定クラスの中で、メソッドに @Bean を付ける

@Controller@RestController@Service@Repository@Component をクラスに付けると、Springがそのクラスを認識し、インスタンス化してコンテナに格納します。

一方、@Configuration@Bean は、少し使い方が異なります。@Configuration を付けた設定クラスの中で、メソッドに @Bean を付けると、そのメソッドが返したインスタンスがコンテナに格納されます

@Configuration
class ConfigurationClass {

  @Bean
  MainService mainService() {
    return new MainService();
  }
}

それぞれの詳しい役割については、「アノテーション編」で解説します。今回は、コンテナへの格納には、このようなアノテーションが使われることを押さえておいてください。

まとめ

「DIの基本編」の内容をまとめます。ポイントは大きく2つです。

  1. DIとは、必要なクラスをSpringが注入する仕組みです。今回の例では、MainController が必要としている MainService をSpringが注入しました
  2. DIが行われるまでの流れは、「インスタンス化 → コンテナへ格納 → 依存関係を確認 → 注入」の4ステップです

このように、「必要なクラスをSpringが準備して、つなぎ合わせる」という考え方が、DIの基本です。

では、Springは必要なクラスをどのような方法で注入するのでしょうか。Spring Bootでは、フィールドやセッター、コンストラクタなど、いくつかの注入方法があります。

次の章では、それぞれの違いと、実務で推奨される注入方法について解説します。