@Serviceはある。なぜ見つからない? 定義・探索・読み込みをつなぐ。

広告 実装・エラー解決

NoSuchBeanDefinitionExceptionの対処法|@Serviceがあるのに見つからないとき

必要なクラスに@Serviceを付けたのに、Springが「Beanが見つからない」と言う。設定ファイルもプロジェクトにある。何が足りないのか分からず、アノテーションを追加して回っていないでしょうか。

この記事は、特にXML設定とアノテーションが混ざるアプリを保守する人向けです。定義があることと、今使っているコンテナへ登録されていることは別として確認します。

要求された型 → 登録方法 → 設定を読み込む入口、の順にたどる。
候補0件なら登録と読み込みを調べます。複数件から選べないなら、登録を増やすのではなく、使う実装の選択を確認してください。

まず「何が見つからないか」をログから抜く

次は読み取り用の説明ログです。

NoSuchBeanDefinitionException:
No qualifying bean of type 'demo.inquiry.SomeService' available

要求された型はdemo.inquiry.SomeServiceです。外側の起動エラーに、これを必要とするControllerやコンストラクタ引数が書かれていれば、それも拾います。型ではなく名前で取得している場合は、要求されたBean名を確認します。

名前が似た別クラスを開かないよう、packageを含む型名まで合わせてください。

@Serviceの次に、探索範囲を見る

このサービスを、コンポーネントスキャンで登録する例を考えます。

package demo.inquiry;

import org.springframework.stereotype.Service;

@Service
public class SomeService {
    public String doSomething() {
        return "Hello from SomeService!";
    }
}

探索するパッケージがdemo.otherだけなら、このクラスはその探索からは登録されません。ファイルを置いたフォルダの印象ではなく、package宣言とスキャン設定を比べます。Spring公式のコンポーネントスキャン

@BeanやXMLの<bean>で登録する方式もあります。アノテーションがないことを見つけたら、追加する前に既存の登録方法を確認します。

XMLの一行が正しくても、読み込まれていなければ登録されない

XMLでの探索指定は、例えば次のようになります。これはcontext名前空間を宣言済みの設定内に置く断片です。

<context:component-scan base-package="demo.inquiry" />

ここでは二つの問いが必要です。

  1. 探索先にSomeServiceが含まれるか。
  2. このXMLを、対象のApplicationContextが実際に読んでいるか。

二つ目を忘れると、XMLの構文を何度直しても変化しません。ファイルがresources配下にあるだけでは、その設定を使うことの証明になりません。

既存アプリの起動設定、XMLのimport、Java設定からの読み込みを追い、このXMLへ到達するかを確認します。複数のコンテナを作る構成なら、登録先と探している側の関係も見ます。Spring公式のコンテナ概要

同じクラスで四つの状態を比べる

独立した検証では、Java 17.0.8とSpring Framework 6.1.1を使い、次の違いを確認しています。ここに挙げた版は再現環境であり、現在の推奨版を示すものではありません。

状態結果直す対象
XMLを読み込み、demo.inquiryを探索SomeServiceを取得できる正常な基準
XMLを読み込むが、demo.missingを探索対象型が0件探索対象
XMLを読み込まない空のコンテナ対象型が0件設定を読み込む入口
同じ型を別名で二つ登録型だけでは一つに選べない実装の選択

検証範囲はBean登録と取得です。Webアプリ全体やProfile、実際の本番設定を検証した結果ではありません。ただ、何を変えると結果が変わるかを、この小さな範囲で切り分けられます。

候補が複数あるエラーを、登録漏れとして直さない

NoUniqueBeanDefinitionExceptionは、要求に合う候補が複数あり、一つに決められない問題です。この例外はNoSuchBeanDefinitionExceptionの派生型なので、親型の名前だけでは区別できません。公式API

具体的な例外名と「何件見つかったか」を読みます。二件あるところへさらに@Serviceを追加しても解決しません。どの実装を使う仕様かを決め、必要な@Qualifierや@Primaryなどの選択を確認します。

必須依存を任意にしたり、newしてSpring管理を避けたりしてエラーだけを消すのも勧めません。本来必要な依存や管理機能が抜けたまま動く可能性があります。

テストだけ失敗するときは、本体と設定を比較する

本体では動くなら、クラスのアノテーションを増やす前に、テストで読み込んでいる設定を見ます。テストが限られた層だけを対象にしていたり、本体とは別のXMLを指定していたりするためです。

Profileや条件付き登録、テストの読み込み範囲まで調べる場合は、No qualifying bean of typeの確認ガイドも参照してください。

調査を引き継ぐときは「要求・登録・読み込み」を残す

ControllerがSomeServiceを要求しています。SomeServiceには@Serviceがあります。探索XMLのbase-packageはdemo.inquiryですが、本体からそのXMLを読み込む入口がまだ確認できていません。次は起動設定とimportを確認します。

「Beanがありません」より、どこまで確認し、どこが空いているか分かる報告です。アノテーションを足す前に、この三点を一行ずつ埋めてください。

DIを含めてアプリの構成を学び直すなら

『作って学ぶ Spring Boot入門』は、Javaの基本文法を使える人が、Spring Bootでの開発を学ぶための候補です。このXMLエラーだけの解決本ではなく、アプリを作りながら構成の理解を広げる目的で選べます。出版社の紹介

脱初心者の本棚を見る(Amazon)

※Amazonのリンクにはアフィリエイトを含みます。

-実装・エラー解決
-, ,