Spring NoSuchBeanDefinitionException
1. 概要
この記事では、Spring org.springframework.beans.factory.NoSuchBeanDefinitionExceptionについて説明します。これは、Springコンテキストでa bean that simply isn’t definedを解決しようとしたときにBeanFactoryによってスローされる一般的な例外です。
この問題の考えられる原因と利用可能な解決策を説明します。
そしてもちろん、例外はあなたがそれらを最も期待しないときに起こります。 the full list of exceptions and solutions in Springを見てください。
参考文献:
2. 原因:依存関係の対象となるタイプのBeanが見つかりません[…]
この例外の最も一般的な原因は、定義されていないBeanを注入しようとすることです。 例–BeanBは共同作業者の配線です–BeanA:
@Component
public class BeanA {
@Autowired
private BeanB dependency;
//...
}
これで、依存関係–BeanB –がSpringコンテキストで定義されていない場合、ブートストラッププロセスはthe no such bean definition exceptionで失敗します。
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org.example.packageB.BeanB]
found for dependency:
expected at least 1 bean which qualifies as
autowire candidate for this dependency.
Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}
その理由はSpringによって明確に示されています:「expected at least 1 bean which qualifies as autowire candidate for this dependency」
BeanBがコンテキストに存在しない理由の1つ– Beanがclasspath scanningによって自動的に取得され、BeanBがBeanとして正しく注釈付けされている場合(@Component、%(t4 )s、@Service、@Controllerなど)–a package that is not scanned by Springで定義できることです。
package org.example.packageB;
@Component
public class BeanB { ...}
クラスパススキャンは次のように構成できます。
@Configuration
@ComponentScan("org.example.packageA")
public class ContextWithJavaConfig {
...
}
Beanが代わりにdefined manuallyによって自動的にスキャンされない場合、BeanBは単に現在のSpringコンテキストで定義されていません。
3. 原因:[…]のフィールド[…]にはタイプ[…]のBeanが必要ですが見つかりませんでした
上記のシナリオのSpring Bootアプリケーションでは、別のメッセージが表示されます。
BeanBがBeanAに配線されているが、定義されていない同じ例を見てみましょう。
@Component
public class BeanA {
@Autowired
private BeanB dependency;
//...
}
この単純なアプリケーションを実行しようとすると、BeanAをロードしようとします。
@SpringBootApplication
public class NoSuchBeanDefinitionDemoApp {
public static void main(String[] args) {
SpringApplication.run(NoSuchBeanDefinitionDemoApp.class, args);
}
}
アプリケーションは、エラーメッセージで起動に失敗します。
***************************
APPLICATION FAILED TO START
***************************
Description:
Field dependency in com.example.springbootmvc.nosuchbeandefinitionexception.BeanA required a bean of type 'com.example.springbootmvc.nosuchbeandefinitionexception.BeanB' that could not be found.
Action:
Consider defining a bean of type 'com.example.springbootmvc.nosuchbeandefinitionexception.BeanB' in your configuration.
ここで、com.example.springbootmvc.nosuchbeandefinitionexceptionは、BeanA、BeanB、およびNoSuchBeanDefinitionDemoAppのパッケージです。
この例のスニペットはthis Github project.にあります
4. 原因:タイプ[…]の修飾Beanが定義されていません
例外の別の原因は、コンテキスト内に1つではなく2つのBean定義が存在することです。 たとえば、インターフェース–IBeanBが2つのBean –BeanB1とBeanB2によって実装されている場合:
@Component
public class BeanB1 implements IBeanB {
//
}
@Component
public class BeanB2 implements IBeanB {
//
}
ここで、BeanAがこのインターフェースを自動配線する場合、Springは2つの実装のどちらを注入するかを認識しません。
@Component
public class BeanA {
@Autowired
private IBeanB dependency;
...
}
また、これにより、BeanFactoryによってNoSuchBeanDefinitionExceptionがスローされます。
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type
[org.example.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2
同様に、Springは配線障害の理由を明確に示しています:“expected single matching bean but found 2”。
ただし、この場合、スローされる正確な例外はNoSuchBeanDefinitionExceptionではなく、サブクラス–the NoUniqueBeanDefinitionExceptionであることに注意してください。 この新しい例外はintroduced in Spring 3.2.1でした。これは、Bean定義が見つからなかった原因と、コンテキスト内で複数の定義が見つかった原因を区別するためです。
この変更前の例外は次のとおりです。
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org.example.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2
ワイヤリングするBeanの名前を正確に指定するための1つのsolution to this problem is to use the @Qualifier annotation:
@Component
public class BeanA {
@Autowired
@Qualifier("beanB2")
private IBeanB dependency;
...
}
これで、Springには、注入するBean(BeanB1またはBeanB2)を決定するのに十分な情報があります(BeanB2のデフォルト名はbeanB2です)。
5. 原因:名前の付いたBeanがありません[…]が定義されています
定義されていないBeanがSpringコンテキストのrequested by nameである場合にも、NoSuchBeanDefinitionExceptionがスローされる可能性があります。
@Component
public class BeanA implements InitializingBean {
@Autowired
private ApplicationContext context;
@Override
public void afterPropertiesSet() {
context.getBean("someBeanName");
}
}
この場合、「someBeanName」のBean定義はなく、次の例外が発生します。
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No bean named 'someBeanName' is defined
繰り返しになりますが、Springは失敗の理由を明確かつ簡潔に示しています:「No bean named X is defined」。
6. 原因:プロキシされたBean
コンテキスト内のBeanがJDKDynamic Proxyメカニズムを使用してプロキシされる場合、the proxy will not extend the target bean(ただし、同じインターフェースを実装します)。
このため、Beanがインターフェイスによって挿入された場合、Beanは正しく配線されます。 ただし、Beanが実際のクラスによって注入される場合、Springはクラスに一致するBean定義を見つけません。これは、プロキシが実際にクラスを拡張しないためです。
Beanがプロキシされる非常に一般的な理由は、Spring transactional support、つまり@Transactionalで注釈が付けられたBeanです。
たとえば、ServiceAがServiceBを注入し、両方のサービスがトランザクションである場合、injecting by the class definitionは機能しません。
@Service
@Transactional
public class ServiceA implements IServiceA{
@Autowired
private ServiceB serviceB;
...
}
@Service
@Transactional
public class ServiceB implements IServiceB{
...
}
同じ2つのサービス、今回は正しくinjecting by the interfaceで問題ありません。
@Service
@Transactional
public class ServiceA implements IServiceA{
@Autowired
private IServiceB serviceB;
...
}
@Service
@Transactional
public class ServiceB implements IServiceB{
...
}
7. 結論
このチュートリアルでは、一般的なNoSuchBeanDefinitionExceptionの考えられる原因の例について、実際にこれらの例外に対処する方法に焦点を当てて説明しました。
これらすべての例外の例の実装は、the GitHub projectにあります。これはEclipseベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。
最後に、Springのthe full list of exceptions and solutionsはブックマークするのに適したリソースかもしれません。