404 Not Found

404 Not Found


nginx

最初のSpring Bootアプリケーション

たった1つの@SpringBootApplicationアノテーションでアプリケーション全体を起動できる—Spring Bootの魔法は3つのアノテーションの巧みな組み合わせにあります。

1. 学ぶ内容


2. 新規プロジェクトに関する実話

(1) ペインポイント:プロジェクトセットアップに半日かかる

Aliceは入社当時, 従来のSpring MVCを使っていた時の経験を思い出しました。プロジェクトを作成するにはweb.xml, applicationContext.xml, spring-mvc.xmlの設定が必要で, TomcatのインストールとJNDIデータソースの設定も必要でした。プロジェクトを起動するだけで半日かかり, 起動順序やBean読み込みの問題のデバッグはおろかありません。

(2) Spring Bootによる解決策

Spring Bootではアノテーション1つとメインメソッド1つだけです。

JAVA
@SpringBootApplication
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

(3) 成果

AliceはSpring Bootを使ってOrderFlowプロジェクトを作成しました。ゼロから最初のAPIレスポンスまで5分しかかかりませんでした。XML不要, 外部コンテナ不要, 起動ログも明確で読みやすい。


3. @SpringBootApplicationアノテーションの分解

(1) 3つのアノテーションの組み合わせ

@SpringBootApplicationは複合アノテーションであり、以下の3つのアノテーションを同時に使用するのと同等です。

100%
graph TB
    A["@SpringBootApplication"] --> B["@SpringBootConfiguration"]
    A --> C["@EnableAutoConfiguration"]
    A --> D["@ComponentScan"]
    B --> B1["クラスを設定Beanの<br/>ソースとしてマーク"]
    C --> C1["クラスパスに基づく<br/>自動設定を起動"]
    D --> D1["同じパッケージツリー内の<br/>コンポーネントをスキャン"]
アノテーション 機能 従来の記述での同等表現
@SpringBootConfiguration 現在のクラスを設定クラスとしてマーク @Configuration
@EnableAutoConfiguration 自動設定の有効化 @EnableAutoConfiguration
@ComponentScan コンポーネントスキャン <context:component-scan>

(2) @ComponentScanのスキャンルール

@ComponentScanはデフォルトでメイン起動クラスを含むパッケージとそのすべてのサブパッケージをスキャンします。

TEXT
com.orderflow ← メイン起動クラスを含むパッケージ
├── OrderFlowApplication.java           ← @SpringBootApplication
├── controller/ ← @ComponentScanでスキャン対象
│   └── OrderController.java
├── service/ ← @ComponentScanでスキャン対象
│   └── OrderService.java
└── repository/ ← @ComponentScanでスキャン対象
    └── OrderRepository.java
com.other ← ⚠️ スキャン対象外!
🔥 よくある間違い: Controllerをメイン起動クラスを含むパッケージの上位または同レベルのパッケージに配置すると, Spring Bootはスキャンせず, 404エラーになります。


4. SpringApplication.run()の起動プロセス

(1) 起動シーケンス

SpringApplication.run()の実行プロセスは慎重に設計されたパイプラインです。

100%
sequenceDiagram
    participant Main as main()
    participant SA as SpringApplication
    participant Ctx as ApplicationContext
    participant Bean as Beans

    Main->>SA: new SpringApplication()
    SA->>SA: プライマリソースの推論
    SA->>SA: Webアプリケーションタイプの確認
    SA->>SA: イニシャライザとリスナーの読み込み
    Main->>SA: run(args)
    SA->>SA: ブートストラップコンテキストの作成
    SA->>SA: 環境の準備
    SA->>SA: バナーの表示
    SA->>Ctx: ApplicationContextの作成
    SA->>Ctx: コンテキストの準備 (ソースの登録)
    SA->>Ctx: コンテキストのリフレッシュ
    Ctx->>Bean: Beanのインスタンス化
    Ctx->>Bean: 自動設定
    SA->>SA: Runnerの呼び出し
    SA->>Main: ApplicationContextを返却

(2) Webアプリケーションタイプの判定

Spring Bootはアプリケーションタイプを自動検出します。

条件 タイプ 使用コンテナ
クラスパスにspring-webmvcが含まれる SERVLET Tomcat / Jetty / Undertow
クラスパスにspring-webfluxはあるがspring-webmvcはない REACTIVE Netty
なし NONE 組み込みコンテナなし

(1) ▶ サンプル:SpringApplication起動のカスタマイズ

JAVA
@SpringBootApplication
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(OrderFlowApplication.class);
        app.setBannerMode(Banner.Mode.CONSOLE);
        app.setWebApplicationType(WebApplicationType.SERVLET);
        app.run(args);
    }
}

出力:

TEXT
// 実行成功

5. 設定ファイル形式

(1) Properties vs. YAML

Spring Bootは2つの設定ファイル形式をサポートしており, 機能的には同等ですが構文が異なります。

(1) ▶ サンプル:application.propertiesの書き方

PROPERTIES
server.port=8080
spring.application.name=orderflow-service
spring.datasource.url=jdbc:mysql://localhost:3306/orderflow
spring.datasource.username=root
spring.datasource.password=secret
spring.jpa.hibernate.ddl-auto=update

出力:

TEXT
// 実行成功

(2) ▶ サンプル:application.ymlの書き方

YAML
server:
  port: 8080

spring:
  application:
    name: orderflow-service
  datasource:
    url: jdbc:mysql://localhost:3306/orderflow
    username: root
    password: secret
  jpa:
    hibernate:
      ddl-auto: update

出力:

TEXT
設定が反映されました。
項目 Properties YAML
階層構造 .で区切る インデントで階層を表現
可読性 シンプルなキー・バリュー, フラット 階層が明確, ネストに適している
リストサポート list[0]=a - aより直感的
重複プレフィックス 毎回記述必要 同レベルでプレフィックスを共有
パース性能 より速い やや遅い (階層の解析が必要)
優先度 同じ 同じ (両方存在する場合propertiesが優先)
⚠️ 注意: YAMLファイルはインデントに非常に敏感です。タブではなくスペースを使用し, 同じレベル内のインデントは一貫させる必要があります。

(2) よく使う設定オプションのクイックリファレンス

設定オプション デフォルト値 説明
server.port 8080 アプリケーションのリスニングポート
spring.application.name アプリケーション名
server.servlet.context-path / コンテキストパス
spring.main.banner-mode console バナー表示モード
spring.jpa.show-sql false SQLの出力
logging.level.root INFO グローバルログレベル

6. バナーと起動ログのカスタマイズ

(1) カスタムバナー

src/main/resources/banner.txtにカスタムバナーを配置すると、Spring Boot起動時に自動的に表示されます。

(1) ▶ サンプル:OrderFlowカスタムバナー

TEXT
  ____              _         ____  __
 / ___| _ __   __ _| |_ ___  / ___||  |  ___  _ __
| |    | '_ \ / _` | __/ _ \ \___ \|  |/ _ \| '_ \
| |___ | | | | (_| | ||  __/  ___) |  | (_) | | | |
 \____||_| |_|\__,_|\__\___| |____/|_|\___/|_| |_|

:: OrderFlow Service ::  v${application.version:1.0.0}
:: Spring Boot ${spring-boot.version} ::

出力:

TEXT
実行成功

(2) 起動ログの読み方

TEXT
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_  __ _   \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` |  \ \ \ \
 \\/  ___)| |_)| | | | | | || (_| |  / / / /
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.2.5)

2024-01-15 10:00:01.123  INFO 12345 --- [main] c.o.OrderFlowApplication : Starting OrderFlowApplication
2024-01-15 10:00:03.456  INFO 12345 --- [main] o.s.b.w.e.t.TomcatWebServer : Tomcat initialized with port 8080 (http)
2024-01-15 10:00:04.789  INFO 12345 --- [main] o.s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories
2024-01-15 10:00:06.012  INFO 12345 --- [main] c.o.OrderFlowApplication : Started in 5.123 seconds
ログキーワード 意味
Starting OrderFlowApplication アプリケーション起動中
Tomcat initialized with port 組み込みTomcat起動
Bootstrapping Spring Data JPA JPAリポジトリの自動設定
Started in X seconds 起動完了, 所要時間

7. 最初の"Hello OrderFlow"エンドポイントの実行

(1) ▶ サンプル:Hello OrderFlowコントローラ

JAVA
package com.orderflow.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;

@RestController
public class HelloController {

    @GetMapping("/api/hello")
    public Map<String, String> hello() {
        return Map.of(
            "message", "Hello, OrderFlow!",
            "timestamp", java.time.Instant.now().toString()
        );
    }
}

出力:

TEXT
HTTP 200 OK
Content-Type: application/json

{"status":"success","data":{}}
💻 出力:

TEXT
$ curl http://localhost:8080/api/hello
{"message":"Hello, OrderFlow!","timestamp":"2024-01-15T10:00:00Z"}

(2) ▶ サンプル:@Valueを使った設定インジェクション

JAVA
@RestController
public class HelloController {

    @Value("${spring.application.name}")
    private String appName;

    @GetMapping("/api/info")
    public Map<String, String> info() {
        return Map.of(
            "application", appName,
            "javaVersion", System.getProperty("java.version")
        );
    }
}

出力:

TEXT
// 実行成功

8. 総合サンプル:OrderFlowの完全な起動設定

JAVA
// src/main/java/com/orderflow/OrderFlowApplication.java
package com.orderflow;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OrderFlowApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderFlowApplication.class, args);
    }
}

// src/main/java/com/orderflow/controller/OrderFlowController.java
package com.orderflow.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.Instant;
import java.util.Map;

@RestController
public class OrderFlowController {

    @Value("${spring.application.name:orderflow}")
    private String appName;

    @GetMapping("/api/health")
    public Map<String, Object> health() {
        return Map.of(
            "status", "UP",
            "application", appName,
            "timestamp", Instant.now()
        );
    }
}
YAML
# src/main/resources/application.yml
server:
  port: 8080

spring:
  application:
    name: orderflow-service
💻 出力:

TEXT
$ curl http://localhost:8080/api/health
{"status":"UP","application":"orderflow-service","timestamp":"2024-01-15T10:00:00Z"}

❓ よくある質問

Q @SpringBootApplicationはどのクラスにも適用できますか?
A 技術的には可能ですが, ルートパッケージに配置することを強く推奨します。@ComponentScanはデフォルトでメインクラスを含むパッケージとそのサブパッケージをスキャンするため, 誤った場所に配置すると他のBeanが発見されない可能性があります。
Q propertiesとYAMLは共存できますか?
A はい, ただし重複するプロパティがある場合, propertiesの定義が優先されます。混乱を避けるためどちらか1つを使用することを推奨します。YAMLの方が階層が明確なため推奨します。
Q バナーを無効にするにはどうすればよいですか?
A 設定ファイルでspring.main.banner-mode=offを設定するか, コードでapp.setBannerMode(Banner.Mode.OFF)を指定してください。
Q 起動時に"Port 8080 already in use"エラーが出る場合どうすればよいですか?
A server.port=8081に変更するか, ポートを使用しているプロセスを特定して終了してください。lsof -i :8080 (Linux/Mac)またはnetstat -ano | findstr 8080 (Windows)を使用します。
Q 起動が遅い原因は何ですか?
A よくある原因:1)クラスパスが大きすぎてスキャンに時間がかかる;2)Hibernate ddl-auto=validateがリモートデータベースへの接続で遅延する;3)不要な自動設定が除外されていない。--debugモードで詳細を確認してください。
Q メインエントリクラスの指定方法は?
A Mavenプラグインの<mainClass>で設定するか, IDEでmainメソッドを含むクラスを直接実行してください。Spring Boot Maven Pluginはパッケージ時に自動検出します。

📖 まとめ


📝 練習問題

  1. 基本問題 (難易度 ⭐):Spring Bootプロジェクトを作成し, banner.txtをカスタマイズし, サービスポートを9090に変更し, 起動後に/api/helloエンドポイントにアクセスできることを確認してください。

  2. 応用問題 (難易度 ⭐⭐):SpringApplication.run()SpringApplicationBuilderに置き換え, バナーを無効にし, ログレベルをDEBUGに設定し, アプリケーション名とJavaバージョンを返す/api/app-infoエンドポイントをカスタマイズしてください。

  3. チャレンジ問題 (難易度 ⭐⭐⭐):StartupListenerApplicationListener<ApplicationStartedEvent>を実装し, アプリケーションの起動時間を記録してログに出力してください。Spring Bootのイベントメカニズムの設計意図について考察してください。

Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%