こんにちは、かつコーチです。
Spring Bootを始めたばかりの頃は、mvn spring-boot:runや./gradlew bootRunを叩いた瞬間に真っ赤なスタックトレースが流れてきて、心が折れそうになった経験がある人も多いと思います。
Spring Bootのエラーメッセージは長くて読みにくく見えますが、実はパターンが決まっているものがほとんどです。
今回は、初心者がつまずきやすい代表的なエラーを4つに絞って、原因の見つけ方と対処法を整理します。
Spring Bootのエラーメッセージの読み方
エラーの構造を理解する
Spring Bootの起動エラーは、上から順に読むと迷子になります。
実は一番重要な情報は、スタックトレースの中盤にあるCaused by(原因の連鎖を示す行)にあることがほとんどです。
Description:
Field bookService in com.example.demo.controller.BookController required
a bean of type 'com.example.demo.service.BookService' that could not be found.
Action:
Consider defining a bean of type 'com.example.demo.service.BookService' in
your configuration.
Spring Bootは親切なことに、エラーの最後にDescription(何が起きたか)とAction(どう直せばいいか)をまとめて表示してくれます。
まずスタックトレースの一番下までスクロールして、この2つを読む癖をつけると原因特定が早くなります。
コンソールログの流れを追う
エラーが起きた瞬間だけでなく、その少し上のログにも注目してください。
どのクラスをロードしようとした直後にエラーが起きたかが分かれば、原因のクラスを絞り込めます。
よくあるエラー4選
BeanCreationException:Beanが見つからない
BeanCreationExceptionは、Spring Bootが管理する部品(Bean:DIコンテナに登録され、必要な場所へ自動で注入されるオブジェクト)を作ろうとして失敗したときに出るエラーです。
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'bookController': Unsatisfied dependency expressed through field
'bookService'
最も多い原因は、@Serviceや@Componentの付け忘れです。
// ❌Before:@Serviceを付け忘れている
public class BookService {
public List<Book> findAll() {
return bookRepository.findAll();
}
}
// ✅After:@Serviceでコンポーネントとして登録する
@Service
public class BookService {
public List<Book> findAll() {
return bookRepository.findAll();
}
}
実際に僕がこのエラーに遭遇したときは、@Serviceは付いていたのに、クラスがSpring Bootの@SpringBootApplicationが置かれたパッケージの外側にあったのが原因でした。
Spring Bootは、起動クラスと同じパッケージ以下しか自動でスキャンしないため、パッケージ構成にも注意が必要です。
NoSuchBeanDefinitionException:候補が複数ある、または存在しない
NoSuchBeanDefinitionExceptionは、注入しようとしたBeanの実装が見つからない、あるいはインターフェースの実装クラスが2つ以上あって選べないときに発生します。
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.example.demo.service.PaymentService' available:
expected single matching bean but found 2
実装が複数ある場合は、@Primaryで優先クラスを指定するか、@Qualifierで明示的に指定します。
@Service
@Qualifier("creditCardPayment")
public class CreditCardPaymentService implements PaymentService {
}
@RestController
public class OrderController {
private final PaymentService paymentService;
public OrderController(@Qualifier("creditCardPayment") PaymentService paymentService) {
this.paymentService = paymentService;
}
}
Port already in use:ポート競合
開発中に何度も遭遇するのがこのエラーです。
Web server failed to start. Port 8080 was already in use.
前回起動したSpring Bootプロセスが正常に終了していない場合によく起きます。
Macであれば以下のコマンドでポートを使っているプロセスを特定し、終了できます。
lsof -i :8080
kill -9 <PID>
毎回調べるのが面倒な場合は、application.propertiesでポート番号を変更してしまうのも実務ではよくある対応です。
server.port=8081
Whitelabel Error Page:エンドポイントが見つからない
ブラウザで画面を開いたときに真っ白なエラーページが表示されるのがこのケースです。
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this
as a fallback.
多くの場合、@GetMappingのパスの綴りミスか、@RestControllerと@Controllerの混同が原因です。
@RestControllerで画面(HTML)を返そうとしても、戻り値がそのまま文字列としてブラウザに返されるだけで、Thymeleafのテンプレートは表示されません。
画面表示には@Controller、JSON等のAPIレスポンスには@RestControllerという使い分けを、まず疑ってみるとよいでしょう。
応用・一歩先の使い方
デバッグレベルのログを有効にする
エラーメッセージだけで原因が分からないときは、application.propertiesにデバッグ設定を追加すると、Spring Bootの起動プロセスがどこまで進んで失敗したかを詳しく確認できます。
logging.level.org.springframework=DEBUG
原因が絞り込めたら、この設定は必ず元に戻しておきましょう。
ログ量が膨大になり、本番環境では性能にも影響します。
まとめ
この記事のポイント
- スタックトレースはDescriptionとActionを最後まで読むと原因が分かりやすい
- BeanCreationExceptionはアノテーションの付け忘れやパッケージ配置ミスが多い
- 実装クラスが複数あるときは
@Primaryや@Qualifierで解決する - ポート競合は
lsofでプロセスを特定するか、ポート番号を変更する - Whitelabel Error Pageは
@Controllerと@RestControllerの混同を疑う
次に読むべき記事
- 循環参照エラーの原因と解決法
- @Component・@Service・@Repositoryの違い
- SLF4Jでログ出力を整える
タグ: Spring Boot, 初心者向け, エラー解決