メインコンテンツまでスキップ

Spring Boot

Java Servletを学んだ次は、Spring Bootの設計思想を理解しましょう。Spring Bootは単なる便利ツールではなく、「エンタープライズJava開発をどうあるべきか」という哲学を持ったフレームワークです。


Spring Bootが生まれた背景

Spring Frameworkの課題

Spring Frameworkは強力でしたが、設定が複雑でした。

┌─────────────────────────────────────────────────────────────┐
│ Spring Framework時代の課題 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. XML地獄 │
│ ・数百行のXML設定ファイル │
│ ・Bean定義、データソース、トランザクション... │
│ ・設定ミスに気づきにくい │
│ │
│ 2. 依存関係の複雑さ │
│ ・どのライブラリが必要か分からない │
│ ・バージョン互換性の確認が必要 │
│ ・「動くまで試行錯誤」 │
│ │
│ 3. デプロイの手間 │
│ ・WARファイルを作成 │
│ ・外部のTomcatにデプロイ │
│ ・環境ごとに設定を変更 │
│ │
│ 結果: 「Hello World」まで数時間かかることも │
│ │
└─────────────────────────────────────────────────────────────┘

Spring Bootの解決策

┌─────────────────────────────────────────────────────────────┐
│ Spring Bootのアプローチ │
├─────────────────────────────────────────────────────────────┤
│ │
│ 「設定より規約」(Convention over Configuration) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ "合理的なデフォルト" を提供し、 │ │
│ │ 必要な部分だけカスタマイズできるようにする │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Before: 全てを明示的に設定 │
│ After: デフォルトで動く、変えたい部分だけ設定 │
│ │
└─────────────────────────────────────────────────────────────┘

設計思想

1. Opinionated Defaults(主張のあるデフォルト)

Spring Bootは「こうあるべき」という意見を持っています。

┌─────────────────────────────────────────────────────────────┐
│ Opinionated Defaults │
├─────────────────────────────────────────────────────────────┤
│ │
│ 「選択肢が多すぎると決められない」問題への回答 │
│ │
│ 例: Webサーバー │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 選択肢: Tomcat, Jetty, Undertow, Netty... │ │
│ │ Spring Boot: 「Tomcatで良いでしょう」 │ │
│ │ (変えたければ変えられる) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 例: JSONライブラリ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 選択肢: Jackson, Gson, JSON-B... │ │
│ │ Spring Boot: 「Jacksonで良いでしょう」 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 例: コネクションプール │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 選択肢: HikariCP, Tomcat Pool, DBCP2... │ │
│ │ Spring Boot: 「HikariCPで良いでしょう」 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ メリット: │
│ ・迷わなくて済む │
│ ・業界標準の選択が自動的に適用される │
│ ・本当に必要なときだけカスタマイズ │
│ │
└─────────────────────────────────────────────────────────────┘

2. 自動設定(Auto-configuration)

「クラスパスにあるもの」から「何が必要か」を推論します。

┌─────────────────────────────────────────────────────────────┐
│ 自動設定の思想 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 従来: 「使いたいものを明示的に設定する」 │
│ │
│ 開発者 → 設定ファイル → フレームワーク │
│ 「DataSourceを使います」 │
│ 「JdbcTemplateを使います」 │
│ 「TransactionManagerを使います」 │
│ │
│ Spring Boot: 「あるものから推論する」 │
│ │
│ クラスパス ← 検出 ← Spring Boot → 自動設定 │
│ │
│ 「PostgreSQLドライバがある」 │
│ ↓ 推論 │
│ 「DBを使うんだな」 │
│ ↓ 自動設定 │
│ 「DataSource, JdbcTemplate, TransactionManager を用意」 │
│ │
│ これが「魔法」に見える理由: │
│ ・明示的に書いていないのに動く │
│ ・でも実際は論理的な推論の結果 │
│ │
└─────────────────────────────────────────────────────────────┘

3. Starter(依存関係の抽象化)

「機能」単位で依存関係を考えられるようにします。

┌─────────────────────────────────────────────────────────────┐
│ Starterの思想 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 従来の考え方: 「ライブラリ」単位 │
│ │
│ 「Webアプリを作りたい」 │
│ ↓ │
│ 「spring-webmvc が必要」 │
│ 「jackson-databind が必要」 │
│ 「tomcat-embed-core が必要」 │
│ 「tomcat-embed-el が必要」 │
│ 「hibernate-validator が必要」 │
│ ... (10個以上) │
│ │
│ Starterの考え方: 「機能」単位 │
│ │
│ 「Webアプリを作りたい」 │
│ ↓ │
│ 「spring-boot-starter-web」 │
│ ↓ (内部で必要なものを全て含む) │
│ 完了 │
│ │
│ 抽象化のレベル: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 低レベル: ライブラリA + B + C + D + ... │ │
│ │ ↓ 抽象化 │ │
│ │ 高レベル: 「Web機能」 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

4. 実行可能JAR(Executable JAR)

「アプリケーションは自己完結すべき」という思想。

┌─────────────────────────────────────────────────────────────┐
│ 実行可能JARの思想 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 従来のデプロイモデル: │
│ │
│ アプリ(WAR) → 外部サーバー(Tomcat) → 実行 │
│ │
│ 問題: │
│ ・環境ごとにサーバー設定が異なる │
│ ・「開発環境では動くのに本番で動かない」 │
│ ・サーバーのバージョン管理が別途必要 │
│ │
│ Spring Bootのモデル: │
│ │
│ アプリ(JAR) = コード + サーバー + 設定 │
│ ↓ │
│ java -jar app.jar │
│ ↓ │
│ 実行 │
│ │
│ メリット: │
│ ・環境差異が減る(同じJARがどこでも動く) │
│ ・Docker/Kubernetesと相性が良い │
│ ・12-Factor Appの思想に沿っている │
│ │
│ 12-Factor App との関連: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ V. Build, release, run │ │
│ │ → ビルド成果物は不変 │ │
│ │ VI. Processes │ │
│ │ → ステートレスなプロセス │ │
│ │ VII. Port binding │ │
│ │ → 自己完結型、ポートをバインド │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

アーキテクチャ

Spring Bootの階層構造

┌─────────────────────────────────────────────────────────────┐
│ Spring Boot の階層 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ あなたのアプリケーション │ │
│ │ ・ビジネスロジック │ │
│ │ ・ドメインモデル │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ 利用 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Spring Boot │ │
│ │ ・自動設定 │ │
│ │ ・Starter │ │
│ │ ・組み込みサーバー │ │
│ │ ・Actuator(運用機能) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ 基盤 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Spring Framework │ │
│ │ ・DI/IoC コンテナ │ │
│ │ ・AOP(横断的関心事) │ │
│ │ ・Spring MVC │ │
│ │ ・トランザクション管理 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ 基盤 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Java EE / Jakarta EE 仕様 │ │
│ │ ・Servlet API │ │
│ │ ・JPA │ │
│ │ ・Bean Validation │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

レイヤードアーキテクチャ

Spring Bootが推奨する典型的な構成:

┌─────────────────────────────────────────────────────────────┐
│ レイヤード構成 │
├─────────────────────────────────────────────────────────────┤
│ │
│ HTTP Request │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Controller層 │ │
│ │ ・HTTP の入口/出口 │ │
│ │ ・リクエスト/レスポンスの変換 │ │
│ │ ・入力検証(形式チェック) │ │
│ │ ・ビジネスロジックは持たない │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Service層 │ │
│ │ ・ビジネスロジックの実装 │ │
│ │ ・トランザクション境界 │ │
│ │ ・複数Repositoryの調整 │ │
│ │ ・HTTPを意識しない │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Repository層 │ │
│ │ ・データアクセスの抽象化 │ │
│ │ ・CRUD操作 │ │
│ │ ・SQLやDBの詳細を隠蔽 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ Database │
│ │
│ 各層の責務: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Controller: 「何を受け取り、何を返すか」 │ │
│ │ Service: 「何をするか」(ビジネスルール) │ │
│ │ Repository: 「どこに保存するか」 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

依存性の方向

┌─────────────────────────────────────────────────────────────┐
│ 依存性の方向(重要な原則) │
├─────────────────────────────────────────────────────────────┤
│ │
│ 上位層 → 下位層 への依存のみ許可 │
│ │
│ Controller → Service → Repository │
│ ○ ○ ○ │
│ │
│ 逆方向は禁止: │
│ Controller ← Service ← Repository │
│ × × × │
│ │
│ 理由: │
│ ・変更の影響範囲を限定 │
│ ・テストしやすい(下位層をモック可能) │
│ ・再利用性が高まる │
│ │
│ 例: Repositoryの実装を変更しても │
│ Controller は影響を受けない │
│ │
└─────────────────────────────────────────────────────────────┘

DIコンテナの役割

なぜDIが必要か

┌─────────────────────────────────────────────────────────────┐
│ DIコンテナの必要性 │
├─────────────────────────────────────────────────────────────┤
│ │
│ DIなし: 依存を自分で作る │
│ │
│ UserController │
│ └── new UserService() ← 自分で作る │
│ └── new UserRepository() ← 自分で作る │
│ └── new DataSource() ← 自分で作る │
│ │
│ 問題: │
│ ・テスト時にモックに差し替えられない │
│ ・実装の変更が上位層に波及する │
│ ・ライフサイクル管理が複雑 │
│ │
│ DIあり: 依存を外から注入 │
│ │
│ DIコンテナ │
│ ├── DataSource を作成 │
│ ├── UserRepository を作成(DataSourceを注入) │
│ ├── UserService を作成(Repositoryを注入) │
│ └── UserController を作成(Serviceを注入) │
│ │
│ メリット: │
│ ・各クラスは「何が必要か」だけ宣言 │
│ ・「どう作るか」はコンテナ任せ │
│ ・テスト時は別の実装を注入可能 │
│ │
└─────────────────────────────────────────────────────────────┘

コンストラクタインジェクション

Spring Bootが推奨するDIの方法:

┌─────────────────────────────────────────────────────────────┐
│ コンストラクタインジェクションの利点 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 不変性(Immutability) │
│ ・依存が final になる │
│ ・インスタンス作成後は変更不可 │
│ ・スレッドセーフ │
│ │
│ 2. 必須依存の明示 │
│ ・コンストラクタ引数 = 必須の依存 │
│ ・依存が足りなければコンパイルエラー │
│ │
│ 3. テスト容易性 │
│ ・new でインスタンス化可能 │
│ ・モックを簡単に注入 │
│ │
│ 4. 循環依存の検出 │
│ ・A→B→A のような循環は起動時にエラー │
│ ・設計の問題を早期発見 │
│ │
└─────────────────────────────────────────────────────────────┘

Spring Bootの「魔法」を解く

魔法に見える理由

┌─────────────────────────────────────────────────────────────┐
│ 「魔法」の正体 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 「書いていないのに動く」が魔法に見える │
│ │
│ 実際は: │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 1. クラスパスをスキャン │ │
│ │ 2. 条件に基づいて設定を適用 │ │
│ │ 3. デフォルト値を使用 │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ 例: データベース接続 │
│ │
│ あなたが書くこと: │
│ ・build.gradle に postgresql 依存を追加 │
│ ・application.yml に接続URLを設定 │
│ │
│ Spring Bootが推論すること: │
│ ・「PostgreSQLドライバがある → DBを使う」 │
│ ・「接続URLがある → DataSourceを作る」 │
│ ・「DataSourceがある → JdbcTemplateを作る」 │
│ ・「JdbcTemplateがある → TransactionManagerを作る」 │
│ │
│ これは魔法ではなく「論理的な推論の連鎖」 │
│ │
└─────────────────────────────────────────────────────────────┘

デバッグ方法

┌─────────────────────────────────────────────────────────────┐
│ 自動設定の確認方法 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. --debug オプション │
│ java -jar app.jar --debug │
│ → 適用された/されなかった自動設定を表示 │
│ │
│ 2. Actuator endpoints │
│ /actuator/conditions │
│ → 条件評価の結果を確認 │
│ │
│ 3. ソースコードを読む │
│ spring-boot-autoconfigure モジュール │
│ → 各 *AutoConfiguration クラスを確認 │
│ │
│ 「分からなければソースを読む」が最終手段 │
│ Spring Bootはオープンソース │
│ │
└─────────────────────────────────────────────────────────────┘

まとめ

Spring Bootの設計思想

思想説明
Convention over Configuration合理的なデフォルトを提供
Opinionated Defaults「こうあるべき」という意見を持つ
自動設定クラスパスから必要な設定を推論
Starter機能単位で依存関係を抽象化
実行可能JAR自己完結型のアプリケーション

理解すべきこと

┌─────────────────────────────────────────────────────────────┐
│ Spring Bootを使う上での心構え │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 「魔法」ではなく「推論」 │
│ ・動作原理を理解すればデバッグできる │
│ ・困ったらソースを読む │
│ │
│ 2. デフォルトを知る │
│ ・何が自動設定されるか把握する │
│ ・変えたい部分だけ明示的に設定 │
│ │
│ 3. 全てを使う必要はない │
│ ・必要な機能だけ採用 │
│ ・不要な抽象化は避ける │
│ │
│ 4. トレードオフを理解する │
│ ・便利さ vs 制御性 │
│ ・学習コスト vs 生産性 │
│ │
└─────────────────────────────────────────────────────────────┘

次のステップ