Dev Study
← Spring コース

44. ApplicationContext と Bean 定義

のは で表されます。コンテナが生成して組み立て、管理するを 、何をどう作るかの情報を と呼びます。 は、設定の印・・の3つをまとめたものです。スキャンは、これを付けたクラスのを起点に、配下の 付きクラスを探します。

前の回の自作コンテナは、頼まれた型をその場でたどるだけでした。Spring のコンテナは、設定クラスやスキャンの結果を読んで、何を生成して組み立てるかを知ります。 4.1.1 に だけを入れ、自分のクラスを4つ置いたアプリで定義を数えると 148 で、自分のものは 4 でした。残りの 144 は書いていない定義で、書いていないのに動く部分の実体です。

つまずきやすいのは起点です。起点の兄弟にあたるパッケージに @Component 付きのクラスを置いても、エラーにはならず、単に Bean として存在しません。別の Bean がそれを要求した時点で起動に失敗します。スキャンするパッケージは指定で足せるので、既定の起点がのパッケージ以下だ、という話です。なお 148 はこの最小構成での定義の数で、依存を足せば変わり、生成済みのインスタンスの数でもありません。

点線の付いた語はタップすると説明が出ます。用語集を開く →

コンテナが Bean 定義を集めるまで(Spring Boot 4.1.1 の最小 Web アプリでの実測をもとに作図)
@SpringBootApplication を付けたクラスl11.count.CountApp
このパッケージが起点
3つをまとめて有効にする
コンポーネントスキャン起点の配下の @Component を探す
自動構成枠組みが用意した定義を登録する
設定クラスの印追加の Bean の登録などを書ける
登録される
自分のクラスの定義 4countApp・greetingService ほか
書いていない定義 144Spring Framework と Spring Boot が登録したもの
この定義をもとに生成・組み立て・管理する
ApplicationContext(コンテナ)Bean 定義 148
  • ・148 と 144 は「Spring Boot 4.1.1 + spring-boot-starter-webmvc だけ + 自作4クラス」での値です。依存を1つ足せば変わります
  • ・数えているのは定義の数で、生成済みのインスタンスの数ではありません。144 のうち、どれだけが自動構成から来たかの内訳は数えていません
  • ・メインクラス自身も countApp という Bean になっていました(実測)
  • ・自動構成が何をどう登録するかは、後のレッスンで扱います

サンプルコード(フレームワーク環境が必要なため表示のみ)

// l11/count/CountApp.java — コンテナに「定義はいくつあるか」を尋ねる
package l11.count;

import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Component;

@SpringBootApplication // このクラスのパッケージ(l11.count)がスキャンの起点になる
public class CountApp {
    public static void main(String[] args) {
        // run が返すのがコンテナ(ApplicationContext)
        ConfigurableApplicationContext ctx = SpringApplication.run(CountApp.class, args);
        System.out.println("context class       = " + ctx.getClass().getName());
        System.out.println("beanDefinitionCount = " + ctx.getBeanDefinitionCount());
        String[] names = ctx.getBeanDefinitionNames();
        long mine = Arrays.stream(names)
                .filter(n -> ctx.getType(n) != null && ctx.getType(n).getName().startsWith("l11."))
                .peek(n -> System.out.println("  mine: " + n + " -> " + ctx.getType(n).getName()))
                .count();
        System.out.println("my beans            = " + mine);
        // (先頭5つの定義名を出力する1行を省略)
        ctx.close();
    }
}

@Component class GreetingService {}
@Component("renamed") class URLParser {}
@Component class HTMLRenderer {}

// Spring Boot 4.1.1 / spring-boot-starter-webmvc だけ / JDK 25 での出力
// (ログ行とバナー、省略した1行ぶんの出力を除く。2回起動して2回とも同じ数)
//
// context class       = org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext
// beanDefinitionCount = 148
//   mine: countApp -> l11.count.CountApp
//   mine: greetingService -> l11.count.GreetingService
//   mine: HTMLRenderer -> l11.count.HTMLRenderer
//   mine: renamed -> l11.count.URLParser
// my beans            = 4

図やコードに出てくる用語(タップで説明)

サンプルコードで身につける(5本) →

学んだ概念を、素朴な例から実務寄りの例まで5つの具体例で確認します。

公式ドキュメントで詳しく ↗