T-CREATOR

<div />

Spring Boot の起動が遅い/落ちるを診断:Auto-config レポートと条件分岐の切り分け

Spring Boot の起動が遅い/落ちるを診断:Auto-config レポートと条件分岐の切り分け

Spring Boot アプリケーションの起動時に、予期せぬエラーで落ちたり、異常に時間がかかったりする経験はありませんか?

開発中は問題なく動いていたのに、本番環境や CI/CD パイプラインで突然起動しなくなるケースは珍しくありません。こうした問題の多くは、Spring Boot の自動構成(Auto-configuration)が期待通りに動作していないことが原因です。本記事では、Auto-configuration レポートを活用した診断方法と、条件分岐の仕組みを理解することで、起動トラブルを効率的に解決する手法を解説します。

背景

Spring Boot の自動構成の仕組み

Spring Boot は「設定より規約(Convention over Configuration)」の原則に基づき、開発者が明示的に設定を書かなくても、クラスパス上のライブラリや環境変数を検知して、必要な Bean を自動的に構成してくれます。

この便利な機能を実現しているのが Auto-configuration です。Spring Boot は起動時に、クラスパス上の JAR ファイルや設定情報をスキャンし、条件に応じて様々な Bean を自動生成します。

以下の図は、Spring Boot の起動プロセスと Auto-configuration の動作フローを示しています。

mermaidflowchart TD
  start["アプリケーション起動"] --> scan["クラスパススキャン"]
  scan --> check["条件評価<br/>@Conditional"]
  check --> match{条件一致?}
  match -->|Yes| enable["Auto-configuration<br/>有効化"]
  match -->|No| skip["スキップ"]
  enable --> bean["Bean 生成"]
  skip --> next["次の設定へ"]
  bean --> next
  next --> complete{全設定完了?}
  complete -->|No| check
  complete -->|Yes| done["起動完了"]

図で理解できる要点:

  • Spring Boot は起動時に自動的にクラスパスをスキャンします
  • 各 Auto-configuration クラスには条件(@Conditional)が付与されています
  • 条件が満たされた場合のみ、その設定が有効化されます

Auto-configuration が機能する条件

Spring Boot の Auto-configuration は、様々な条件アノテーションを使って「いつ有効化されるか」を制御しています。

主な条件アノテーションには以下のようなものがあります。

#アノテーション説明
1@ConditionalOnClass特定のクラスがクラスパスに存在する場合に有効化
2@ConditionalOnMissingClass特定のクラスが存在しない場合に有効化
3@ConditionalOnBean特定の Bean が定義されている場合に有効化
4@ConditionalOnMissingBean特定の Bean が定義されていない場合に有効化
5@ConditionalOnProperty特定のプロパティが設定されている場合に有効化
6@ConditionalOnResource特定のリソースファイルが存在する場合に有効化

これらの条件が複雑に組み合わさることで、柔軟な自動構成が実現されていますが、同時にトラブルの原因にもなります。

課題

起動トラブルの典型的なパターン

Spring Boot アプリケーションの起動時に発生するトラブルは、大きく分けて以下の 3 つのパターンに分類できます。

パターン 1:起動が遅い

アプリケーションは最終的に起動するものの、起動に数分以上かかってしまうケースです。

主な原因:

  • 不要な Auto-configuration が大量に実行されている
  • データベース接続のタイムアウト待ち
  • 外部サービスへの接続試行が繰り返されている
  • クラスパススキャンの範囲が広すぎる

パターン 2:起動時にエラーで落ちる

アプリケーションが起動途中で例外を投げて終了してしまうケースです。

典型的なエラーコード:

vbnetError: APPLICATION FAILED TO START

Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class
perlError: BeanCreationException

Description:
Error creating bean with name 'entityManagerFactory' defined in class path resource

Reason: Invocation of init method failed; nested exception is org.hibernate.service.spi.ServiceException

主な原因:

  • 必要な依存ライブラリが不足している
  • 環境変数や設定プロパティが不足している
  • Bean の依存関係が循環している
  • 競合する Bean 定義が存在する

パターン 3:特定環境でのみ起動しない

開発環境では正常に動作するのに、本番環境や Docker コンテナ内でのみ起動しないケースです。

主な原因:

  • 環境変数の設定漏れ
  • プロファイル(dev, prod)の切り替え不備
  • クラスパス上のライブラリバージョンの違い
  • ファイルシステムのパーミッション問題

以下の図は、これらのトラブルパターンと原因の関係性を示しています。

mermaidflowchart LR
  trouble["起動トラブル"] --> slow["パターン1<br/>起動が遅い"]
  trouble --> error["パターン2<br/>エラーで落ちる"]
  trouble --> env["パターン3<br/>特定環境でのみ失敗"]

  slow --> cause1["不要な<br/>Auto-config"]
  slow --> cause2["接続タイムアウト"]

  error --> cause3["依存不足"]
  error --> cause4["Bean 競合"]

  env --> cause5["環境変数不足"]
  env --> cause6["プロファイル<br/>設定ミス"]

図で理解できる要点:

  • 起動トラブルは 3 つの主要パターンに分類される
  • それぞれのパターンに特有の原因がある
  • 原因を特定することが解決への第一歩

なぜ診断が難しいのか

Spring Boot の起動トラブルの診断が難しい理由は、以下の点にあります。

透明性の低さ Auto-configuration は便利ですが、裏で何が起きているのかが見えにくいという特性があります。どの Bean が生成され、どの設定がスキップされたのかを把握するのは容易ではありません。

エラーメッセージの複雑さ スタックトレースが長く、本質的な原因が埋もれてしまうことがあります。また、エラーメッセージが抽象的で、具体的な解決策が分かりにくいケースも多いでしょう。

環境依存性 開発環境と本番環境でクラスパスや環境変数が異なるため、問題の再現が難しい場合があります。

解決策

Auto-configuration レポートの活用

Spring Boot は、起動時にどの Auto-configuration が有効化され、どれがスキップされたのかを詳細に記録する Auto-configuration レポート を生成できます。

このレポートを活用することで、起動プロセスの透明性が大幅に向上し、問題の切り分けが容易になります。

レポート出力の有効化方法

Auto-configuration レポートを出力するには、以下の 2 つの方法があります。

方法 1:コマンドライン引数で指定

起動時に --debug フラグを付けて実行します。

bashjava -jar myapp.jar --debug

方法 2:application.properties で設定

設定ファイルに以下を追加します。

properties# Auto-configuration レポートを有効化
debug=true

方法 3:環境変数で設定

環境変数を使って有効化することも可能です。

bashexport DEBUG=true
java -jar myapp.jar

レポートの読み方

Auto-configuration レポートは、起動ログの中に以下のような形式で出力されます。

sql============================
CONDITIONS EVALUATION REPORT
============================


Positive matches:
-----------------

   DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType' (OnClassCondition)

   DataSourceAutoConfiguration.PooledDataSourceConfiguration matched:
      - @ConditionalOnMissingBean found no beans of type 'javax.sql.DataSource' (OnBeanCondition)
      - @ConditionalOnProperty (spring.datasource.type) matched (OnPropertyCondition)


Negative matches:
-----------------

   RedisAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'org.springframework.data.redis.core.RedisOperations' (OnClassCondition)

   MongoAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'com.mongodb.client.MongoClient' (OnClassCondition)

レポートは Positive matches(有効化された設定)と Negative matches(スキップされた設定)に分かれています。

Positive matches の読み方:

  • どの Auto-configuration が有効化されたかが分かります
  • 各設定の下に、条件が満たされた理由が記載されています
  • 例:DataSourceAutoConfiguration は、必要なクラスがクラスパスに存在したため有効化

Negative matches の読み方:

  • どの Auto-configuration がスキップされたかが分かります
  • スキップされた理由が明記されています
  • 例:RedisAutoConfiguration は、必要なクラスが見つからなかったためスキップ

条件分岐の切り分け手法

Auto-configuration レポートを確認したら、次は具体的な条件の切り分けを行います。

ステップ 1:期待する設定が有効化されているか確認

まず、自分が期待している Auto-configuration が Positive matches に含まれているかを確認します。

もし Negative matches に含まれている場合は、スキップされた理由を確認しましょう。

sqlNegative matches:
-----------------

   DataSourceAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'com.zaxxer.hikari.HikariDataSource'

この例では、HikariCP のクラスが見つからないため、DataSource の自動構成がスキップされています。

ステップ 2:不足している依存関係を特定

Negative matches の理由が @ConditionalOnClass did not find required class の場合、必要なライブラリが不足しています。

解決方法:

依存関係を追加します(Maven の例)。

xml<!-- HikariCP を追加 -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
</dependency>

Yarn や npm ではなく、Spring Boot では Maven や Gradle を使うことが一般的ですが、プロジェクトルールに従い Yarn ベースの設定例も示します。

ただし、Spring Boot プロジェクトでは通常 pom.xml(Maven)または build.gradle(Gradle)を使用します。

ステップ 3:プロパティ設定の確認

@ConditionalOnProperty による条件が満たされていない場合、必要なプロパティを設定します。

sqlNegative matches:
-----------------

   RabbitAutoConfiguration:
      Did not match:
         - @ConditionalOnProperty (spring.rabbitmq.host) did not find property 'spring.rabbitmq.host'

この場合、application.properties または application.yml に設定を追加します。

properties# RabbitMQ の接続設定を追加
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

ステップ 4:競合する Bean の特定

Positive matches に同じ種類の Bean が複数含まれている場合、競合が発生している可能性があります。

rubyError: BeanDefinitionOverrideException

Description:
The bean 'dataSource', defined in class path resource [com/example/config/DatabaseConfig.class], could not be registered. A bean with that name has already been defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class] and overriding is disabled.

解決方法 1:Bean の上書きを許可

properties# Bean の上書きを許可(非推奨)
spring.main.allow-bean-definition-overriding=true

解決方法 2:不要な Auto-configuration を除外

カスタム設定を使う場合は、自動構成を除外します。

java@SpringBootApplication(exclude = {
    DataSourceAutoConfiguration.class
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

デバッグログの強化

Auto-configuration レポートに加えて、Spring のログレベルを調整することで、より詳細な情報を得られます。

properties# Spring Framework のコアロギングを DEBUG レベルに
logging.level.org.springframework=DEBUG

# Auto-configuration の詳細ログ
logging.level.org.springframework.boot.autoconfigure=DEBUG

# Bean の生成プロセスを詳細表示
logging.level.org.springframework.beans.factory=DEBUG

これらの設定により、Bean の生成順序や依存関係の解決プロセスが詳細に記録されます。

ただし、ログ量が膨大になるため、トラブルシューティング時のみ有効化し、本番環境では無効化することを推奨します。

具体例

例 1:DataSource が自動構成されない問題

発生したエラー

アプリケーション起動時に以下のエラーが発生しました。

vbnetError: APPLICATION FAILED TO START

***************************
APPLICATION FAILED TO START
***************************

Description:

Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.

Reason: Failed to determine a suitable driver class


Action:

Consider the following:
	If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
	If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).

診断手順

手順 1:Auto-configuration レポートを有効化

bashjava -jar myapp.jar --debug > startup.log 2>&1

手順 2:レポートの確認

startup.log を開き、DataSource 関連の設定を検索します。

sqlNegative matches:
-----------------

   DataSourceAutoConfiguration:
      Did not match:
         - @ConditionalOnClass found required class 'javax.sql.DataSource'
         - @ConditionalOnProperty (spring.datasource.url) did not find property 'spring.datasource.url' (OnPropertyCondition)

診断結果:

  • 必要なクラスは存在している
  • しかし spring.datasource.url プロパティが設定されていない

解決方法

application.properties にデータベース接続情報を追加します。

properties# データベース接続設定
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

さらに、MySQL ドライバの依存関係が含まれているか確認します(Maven の例)。

xml<!-- MySQL Connector を追加 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

検証

設定を追加した後、再度起動します。

bashjava -jar myapp.jar --debug

Auto-configuration レポートで DataSourceAutoConfiguration が Positive matches に含まれていることを確認します。

sqlPositive matches:
-----------------

   DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'javax.sql.DataSource' (OnClassCondition)
      - @ConditionalOnProperty (spring.datasource.url) matched (OnPropertyCondition)

   DataSourceAutoConfiguration.PooledDataSourceConfiguration matched:
      - @ConditionalOnMissingBean found no beans of type 'javax.sql.DataSource' (OnBeanCondition)

例 2:Redis の自動構成が意図せず有効化される問題

発生した問題

アプリケーションの起動が非常に遅く、ログを確認すると Redis への接続試行が繰り返されていました。

ini2025-01-15 10:23:45.123  WARN 12345 --- [main] io.lettuce.core.cluster.ClusterTopologyRefresh : Unable to connect to [localhost:6379]: Connection refused
2025-01-15 10:23:50.456  WARN 12345 --- [main] io.lettuce.core.cluster.ClusterTopologyRefresh : Unable to connect to [localhost:6379]: Connection refused

しかし、このアプリケーションでは Redis を使用する予定はありませんでした。

診断手順

手順 1:Auto-configuration レポートを確認

bashjava -jar myapp.jar --debug 2>&1 | grep -A 10 "RedisAutoConfiguration"

レポートを確認すると、RedisAutoConfiguration が有効化されていました。

markdownPositive matches:
-----------------

   RedisAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'org.springframework.data.redis.core.RedisOperations' (OnClassCondition)

手順 2:依存関係の確認

pom.xml を確認したところ、別のライブラリが Redis を推移的依存関係として含んでいました。

xml<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>

解決方法

Redis を使用しない場合は、以下のいずれかの方法で対処します。

方法 1:Auto-configuration を除外

java@SpringBootApplication(exclude = {
    RedisAutoConfiguration.class,
    RedisRepositoriesAutoConfiguration.class
})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

方法 2:不要な依存関係を除外

Redis が不要な場合は、依存関係自体を削除します。

xml<!-- 不要な依存関係を削除 -->
<!--
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
-->

方法 3:プロパティで無効化

properties# Redis の自動構成を無効化
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

検証

再度起動し、Auto-configuration レポートで RedisAutoConfiguration が Negative matches に含まれていることを確認します。

sqlNegative matches:
-----------------

   RedisAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'org.springframework.data.redis.core.RedisOperations' (OnClassCondition)

起動時間も大幅に短縮され、Redis への接続試行も行われなくなりました。

例 3:Bean の循環依存エラー

発生したエラー

アプリケーション起動時に以下のエラーが発生しました。

markdownError: BeanCurrentlyInCreationException

***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  serviceA defined in file [ServiceA.class]
↑     ↓
|  serviceB defined in file [ServiceB.class]
↑     ↓
|  serviceC defined in file [ServiceC.class]
└─────┘


Action:

Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans.

以下の図は、Bean の循環依存の状態を示しています。

mermaidflowchart LR
  A["ServiceA"] --> B["ServiceB"]
  B --> C["ServiceC"]
  C --> A

  style A fill:#ffcccc
  style B fill:#ffcccc
  style C fill:#ffcccc

図で理解できる要点:

  • ServiceA、ServiceB、ServiceC が相互に依存している
  • この循環依存により Bean の生成ができない
  • 依存関係を見直す必要がある

診断手順

手順 1:依存関係の確認

各サービスクラスのコンストラクタを確認します。

java// ServiceA.java
@Service
public class ServiceA {
    private final ServiceB serviceB;

    // ServiceB に依存
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}
java// ServiceB.java
@Service
public class ServiceB {
    private final ServiceC serviceC;

    // ServiceC に依存
    public ServiceB(ServiceC serviceC) {
        this.serviceC = serviceC;
    }
}
java// ServiceC.java
@Service
public class ServiceC {
    private final ServiceA serviceA;

    // ServiceA に依存(循環!)
    public ServiceC(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

解決方法

循環依存を解消するには、以下の方法があります。

方法 1:@Lazy アノテーションを使用

循環の一部を遅延初期化にします。

java// ServiceC.java を修正
@Service
public class ServiceC {
    private final ServiceA serviceA;

    // @Lazy で遅延初期化
    public ServiceC(@Lazy ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

方法 2:Setter インジェクションを使用

コンストラクタインジェクションから Setter インジェクションに変更します。

java// ServiceC.java を修正
@Service
public class ServiceC {
    private ServiceA serviceA;

    // Setter インジェクション
    @Autowired
    public void setServiceA(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

方法 3:設計を見直す(推奨)

根本的な解決として、依存関係の設計を見直します。

java// 共通インターフェースを抽出
public interface SharedService {
    void execute();
}

// ServiceA を修正
@Service
public class ServiceA implements SharedService {
    // ServiceB への依存を削除

    @Override
    public void execute() {
        // 実装
    }
}

この例では、循環依存を避けるために、共通のインターフェースを抽出したり、依存関係を一方向にするなどの設計変更を行います。

例 4:プロファイル別の起動トラブル

発生した問題

開発環境(dev プロファイル)では正常に起動するが、本番環境(prod プロファイル)では起動しない問題が発生しました。

診断手順

手順 1:プロファイル別の設定を確認

bash# dev プロファイルで起動(成功)
java -jar myapp.jar --spring.profiles.active=dev --debug

# prod プロファイルで起動(失敗)
java -jar myapp.jar --spring.profiles.active=prod --debug

手順 2:Auto-configuration レポートの比較

dev と prod のレポートを比較します。

bash# dev のレポートを保存
java -jar myapp.jar --spring.profiles.active=dev --debug > dev-report.log 2>&1

# prod のレポートを保存
java -jar myapp.jar --spring.profiles.active=prod --debug > prod-report.log 2>&1

# 差分を確認
diff dev-report.log prod-report.log

手順 3:設定ファイルの確認

application-prod.properties を確認すると、必要な設定が不足していました。

properties# application-prod.properties(不完全)
spring.datasource.url=jdbc:mysql://prod-db-server:3306/mydb
# username と password が不足!

解決方法

不足している設定を追加します。

properties# application-prod.properties(完全版)
spring.datasource.url=jdbc:mysql://prod-db-server:3306/mydb
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 接続プール設定
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5

環境変数で機密情報を管理します。

bash# 環境変数を設定
export DB_USERNAME=prod_user
export DB_PASSWORD=secure_password

# アプリケーションを起動
java -jar myapp.jar --spring.profiles.active=prod

まとめ

Spring Boot アプリケーションの起動トラブルは、Auto-configuration の仕組みを理解し、適切な診断ツールを活用することで効率的に解決できます。

本記事で紹介した手法をまとめます。

診断の基本ステップ:

#ステップ実施内容
1Auto-configuration レポートを有効化--debug フラグまたは debug=true を設定
2Positive / Negative matches を確認どの設定が有効化/スキップされたかを把握
3条件不一致の原因を特定クラス不足、プロパティ不足、Bean 競合などを確認
4必要な修正を実施依存関係追加、設定追加、除外設定など
5再度レポートで検証期待する設定が有効化されたことを確認

重要なポイント:

Auto-configuration レポートは、起動プロセスを可視化する最も強力なツールです。トラブル発生時は必ず最初に確認しましょう。

条件アノテーション(@ConditionalOnClass、@ConditionalOnProperty など)の仕組みを理解することで、なぜ特定の設定が有効化されないのかを論理的に推測できます。

開発環境と本番環境の差異(クラスパス、環境変数、プロファイル)に注意を払い、プロファイル別の設定を適切に管理することが重要です。

不要な Auto-configuration は明示的に除外することで、起動時間の短縮とトラブルの予防につながりますね。

これらの手法を活用することで、Spring Boot アプリケーションの起動トラブルを迅速に解決し、開発効率を大幅に向上させることができるでしょう。

関連リンク

;