mcpField
D
debug:spring-boot

debug:spring-boot

github.com/snakeo開発941 回インストール41 週間前に更新
リポジトリ

debug:spring-boot について

debug:spring-bootは、Spring Bootアプリケーションの問題を調べる開発者向けのトラブルシューティングガイドです。よくある障害、診断ツール、再現から修正・検証までの手順を扱います。

主な機能

  1. 01Beanと起動エラーの診断
  2. 02JPA、セキュリティ、設定の問題を解説
  3. 03ActuatorとJVM診断ツールの活用
  4. 044段階のデバッグ手順
  5. 05Maven、Gradle、Docker、Kubernetesのコマンド

活用シーン

  1. 01アプリケーションコンテキストの起動失敗を調査
  2. 02データベースやトランザクションの問題を追跡
  3. 03デプロイ済みSpring Bootサービスをデバッグ
  4. 04テストで問題を再現し修正を検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/snakeo/claude-debug-and-refactor-skills-plugin --skill debug:spring-boot

インストール後、Claude Code で/debug:spring-bootを入力して読み込まれたか確認します。

詳細

Spring Boot デバッグガイド

あなたは Spring Boot のデバッグに精通しています。問題を効率的に診断して解決するため、この体系的な手順に従ってください。

よくあるエラーパターン

1. NoSuchBeanDefinitionException

症状:

  • 「フィールド xyz で必要な型 'X' の Bean が見つかりませんでした」
  • 「型 'X' の適格な Bean がありません」

デバッグ手順:

  1. クラスに @Component、@Service、@Repository、または @Controller アノテーションが付いていることを確認します
  2. クラスが @ComponentScan によるスキャン対象のパッケージ内にあることを確認します(@SpringBootApplication クラスのパッケージ内、またはその配下である必要があります)
  3. @Bean メソッドを持つ @Configuration クラスが読み込まれていることを確認します
  4. Bean が除外される原因となる条件付きアノテーション(@ConditionalOnProperty、@Profile)がないか確認します
  5. @Qualifier の修飾子名に誤字がないか確認します

簡単な解決策:

// Ensure main class is at root package
@SpringBootApplication
public class Application { ... }

// Explicit component scan if needed
@ComponentScan(basePackages = {"com.example.main", "com.example.other"})

// Check bean registration
@Autowired
private ApplicationContext context;
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
2. アプリケーションの起動失敗

症状:

  • 「Web サーバーを起動できませんでした。ポート 8080 はすでに使用されています」
  • 「アプリケーションの実行に失敗しました」
  • コンテキストの初期化エラー

デバッグ手順:

  1. ポートの競合を確認します:lsof -i :8080 または netstat -an | grep 8080
  2. スタックトレース全体を確認し、根本原因を特定します(Spring のバナーより上までスクロールします)
  3. JPA を使用している場合は、データベースへの接続を確認します
  4. 必要な環境変数がすべて設定されていることを確認します
  5. pom.xml または build.gradle に依存関係の不足がないか確認します

簡単な解決策:

# Change port if in use
server.port=8081

# Enable debug startup logging
debug=true
logging.level.org.springframework=DEBUG

# Fail fast on missing properties
spring.main.allow-bean-definition-overriding=false
3. 循環依存

症状:

  • 「アプリケーションコンテキスト内の一部の Bean の依存関係が循環しています」
  • 「要求された Bean は現在作成中です」

デバッグ手順:

  1. エラーメッセージの依存関係の連鎖を確認します(A -> B -> C -> A)
  2. どの依存関係を解消できるか特定します
  3. 設計のリファクタリングが必要か検討します

簡単な解決策:

// Option 1: Use @Lazy on one dependency
@Autowired
@Lazy
private ServiceB serviceB;

// Option 2: Use setter injection
private ServiceB serviceB;

@Autowired
public void setServiceB(ServiceB serviceB) {
    this.serviceB = serviceB;
}

// Option 3: Refactor to event-based communication
@EventListener
public void handleEvent(CustomEvent event) { ... }
4. JPA/Hibernate の問題

症状:

  • 「実際のトランザクションが利用可能な EntityManager がありません」
  • LazyInitializationException
  • 「テーブルが存在しません」/スキーマ検証エラー
  • N+1 クエリの問題

デバッグ手順:

  1. SQL ロギングを有効にして、実際のクエリを確認します
  2. @Transactional の配置を確認します(public メソッドに付与する必要があります)
  3. エンティティのリレーションシップとカスケードタイプを確認します
  4. データベースのスキーマがエンティティ定義と一致していることを確認します

簡単な解決策:

# Enable SQL debugging
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

# Schema handling
spring.jpa.hibernate.ddl-auto=validate  # Recommended for debugging
spring.jpa.hibernate.ddl-auto=update    # Auto-update schema (dev only)
// Fix LazyInitializationException
@Transactional(readOnly = true)
public Entity getWithChildren(Long id) {
    Entity e = repository.findById(id).orElseThrow();
    e.getChildren().size(); // Force initialization
    return e;
}

// Or use EntityGraph
@EntityGraph(attributePaths = {"children", "children.grandchildren"})
Optional<Entity> findById(Long id);
5. セキュリティ設定の問題

症状:

  • すべてのエンドポイントで 403 Forbidden が発生する
  • 認証が機能しない
  • CORS エラー
  • CSRF トークンの問題

デバッグ手順:

  1. セキュリティのデバッグロギングを有効にします
  2. フィルターチェーンの順序を確認します
  3. 認証プロバイダーの設定を確認します
  4. SecurityFilterChain Bean の設定を確認します

簡単な解決策:

# Enable security debugging
logging.level.org.springframework.security=DEBUG
logging.level.org.springframework.security.web.FilterChainProxy=DEBUG
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/actuator/health").permitAll()
                .anyRequest().authenticated()
            )
            .csrf(csrf -> csrf.disable()) // Only for APIs with token auth
            .cors(Customizer.withDefaults());
        return http.build();
    }
}
6. プロパティのバインド失敗

症状:

  • 「'x.y.z' のプロパティのバインドに失敗しました」
  • 「プレースホルダーを解決できませんでした」
  • 設定値が読み込まれない

デバッグ手順:

  1. プロパティファイルの場所を確認します(src/main/resources)
  2. プロパティ名が完全に一致していることを確認します(大文字と小文字を区別し、ハイフンとキャメルケースにも注意します)
  3. 有効なプロファイルを確認します(spring.profiles.active)
  4. @ConfigurationProperties のプレフィックスが一致していることを確認します

簡単な解決策:

// Debug property sources
@Autowired
private Environment env;

@PostConstruct
public void debugProperties() {
    System.out.println("Active profiles: " + Arrays.toString(env.getActiveProfiles()));
    System.out.println("Property value: " + env.getProperty("my.property"));
}

// Ensure @ConfigurationProperties is scanned
@EnableConfigurationProperties(MyProperties.class)
@SpringBootApplication
public class Application { ... }
# Add to see property resolution
logging.level.org.springframework.boot.context.properties=DEBUG

デバッグツール

Spring Boot Actuator

実行時の診断に不可欠です:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# Expose all actuator endpoints (dev only)
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always

主なエンドポイント:

  • /actuator/health - アプリケーションのヘルスステータス
  • /actuator/beans - 登録済みのすべての Bean
  • /actuator/env - 環境プロパティ
  • /actuator/mappings - リクエストマッピング
  • /actuator/configprops - 設定プロパティ
  • /actuator/conditions - 自動設定レポート
リモート JVM デバッグ
# Maven
mvn spring-boot:run -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"

# Gradle
./gradlew bootRun --debug-jvm

# Java directly
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005 -jar app.jar

# Docker Compose
environment:
  - JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005
ports:
  - "5005:5005"
Logback/Log4j2 の設定

src/main/resources/logback-spring.xml を作成します:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    
    <logger name="org.springframework.web" level="DEBUG"/>
    <logger name="org.hibernate.SQL" level="DEBUG"/>
    <logger name="com.yourapp" level="DEBUG"/>

    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>
Spring Boot DevTools
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

機能:

  • コード変更時の自動再起動
  • LiveReload のブラウザー連携
  • 開発環境でのプロパティバインドの緩和
  • H2 コンソールの自動設定
IntelliJ IDEA Spring デバッガー(2025.2+)
  • 読み込み済みの有効な Bean とそのメタデータを表示します
  • .properties/.yaml ファイル内に実際のプロパティ値をインライン表示します
  • Bean のスコープ、プロファイル、コンテキストを調べます
  • データベース接続を表示します
  • Bean 定義に移動します

4 つのフェーズ(Spring Boot 固有)

フェーズ 1:情報を収集する
# Check application logs
tail -f logs/spring.log

# Check JVM status
jps -lv
jstat -gc <pid>
jstack <pid>

# Check actuator endpoints
curl localhost:8080/actuator/health
curl localhost:8080/actuator/env
curl localhost:8080/actuator/beans
フェーズ 2:再現して切り分ける
// Create minimal test case
@SpringBootTest(classes = {TestConfig.class, ProblemBean.class})
class IsolatedTest {
    @Autowired
    private ProblemBean bean;

    @Test
    void reproduceIssue() {
        // Minimal steps to reproduce
    }
}

// Use test slices for faster isolation
@WebMvcTest(ProblemController.class)
@DataJpaTest
@JsonTest
フェーズ 3:根本原因を診断する
// Add diagnostic logging
@Aspect
@Component
public class DiagnosticAspect {
    private static final Logger log = LoggerFactory.getLogger(DiagnosticAspect.class);

    @Around("execution(* com.yourapp.service.*.*(..))")
    public Object logMethodExecution(ProceedingJoinPoint jp) throws Throwable {
        log.debug("Entering: {}", jp.getSignature());
        try {
            Object result = jp.proceed();
            log.debug("Exiting: {} with result: {}", jp.getSignature(), result);
            return result;
        } catch (Exception e) {
            log.error("Exception in {}: {}", jp.getSignature(), e.getMessage());
            throw e;
        }
    }
}
フェーズ 4:修正して検証する
# Run targeted tests
./mvnw test -Dtest=ProblemTest

# Run full test suite
./mvnw verify

# Check for regressions
./mvnw test -Dtest=*IntegrationTest

コマンド早見表

Maven
# Run with debug logging
./mvnw spring-boot:run -Dspring-boot.run.arguments="--debug"

# Run with specific profile
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev

# Skip tests and run
./mvnw spring-boot:run -DskipTests

# Generate dependency tree
./mvnw dependency:tree

# Check for dependency conflicts
./mvnw dependency:analyze

# Force update dependencies
./mvnw clean install -U
Gradle
# Run with debug logging
./gradlew bootRun --args='--debug'

# Run with specific profile
./gradlew bootRun --args='--spring.profiles.active=dev'

# Show dependencies
./gradlew dependencies

# Refresh dependencies
./gradlew build --refresh-dependencies
Docker
# Check container logs
docker logs <container_name> -f

# Shell into container
docker exec -it <container_name> /bin/sh

# Check container health
docker inspect --format='{{.State.Health.Status}}' <container_name>

# View container environment
docker exec <container_name> env | grep SPRING
Kubernetes
# Get pod logs
kubectl logs <pod-name> -f

# Get previous container logs (after crash)
kubectl logs <pod-name> --previous

# Describe pod for events
kubectl describe pod <pod-name>

# Port forward for debugging
kubectl port-forward <pod-name> 5005:5005

# Shell into pod
kubectl exec -it <pod-name> -- /bin/sh
データベースのデバッグ
# Connect to PostgreSQL
psql -h localhost -U user -d database

# Show running queries
SELECT * FROM pg_stat_activity WHERE state = 'active';

# Connect to MySQL
mysql -h localhost -u user -p database

# Show process list
SHOW PROCESSLIST;
JVM の診断
# List Java processes
jps -lv

# Thread dump
jstack <pid> > thread_dump.txt

# Heap dump
jmap -dump:format=b,file=heap.hprof <pid>

# GC stats
jstat -gcutil <pid> 1000

# JVM flags
jinfo -flags <pid>

マイクロサービスのデバッグ

分散トレーシング

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>
<dependency>
    <groupId>io.zipkin.reporter2</groupId>
    <artifactId>zipkin-reporter-brave</artifactId>
</dependency>
# Zipkin configuration
management.tracing.sampling.probability=1.0
management.zipkin.tracing.endpoint=http://zipkin:9411/api/v2/spans
相関 ID
@Component
public class CorrelationIdFilter extends OncePerRequestFilter {
    private static final String CORRELATION_ID = "X-Correlation-ID";

    @Override
    protected void doFilterInternal(HttpServletRequest request,
            HttpServletResponse response, FilterChain chain) {
        String correlationId = request.getHeader(CORRELATION_ID);
        if (correlationId == null) {
            correlationId = UUID.randomUUID().toString();
        }
        MDC.put("correlationId", correlationId);
        response.setHeader(CORRELATION_ID, correlationId);
        try {
            chain.doFilter(request, response);
        } finally {
            MDC.remove("correlationId");
        }
    }
}

よくある落とし穴

  1. private メソッドに対する @Transactional - 動作しません。public にする必要があります
  2. 同じクラス内から @Transactional メソッドを呼び出す - プロキシを迂回するため、トランザクションが適用されません
  3. カスタム実装に @Repository がない - 例外変換が適用されません
  4. コンストラクター内の @Value - フィールドへの注入がまだ行われていません。@PostConstruct を使用してください
  5. @Autowired を付けた static フィールド - 注入されません
  6. basePackages を指定しない @ComponentScan - 現在のパッケージとその配下のみをスキャンします
  7. 誤った場所にある application.properties - src/main/resources のルートに置く必要があります
  8. プロファイル固有の設定が読み込まれない - spring.profiles.active が設定されているか確認してください

さらに読む

コメント