SpringのNoSuchBeanDefinitionException
例外について、以下を説明してください:
この投稿は、Springを使用するアプリケーションでのNoSuchBeanDefinitionException
の発生に関する包括的なQ&Aになるように設計されています。
NoSuchBeanDefinitionException
のjavadoc の説明
定義が見つからないBeanインスタンスを
BeanFactory
に要求するとスローされる例外。これは、存在しないBean、一意でないBean、またはBean定義が関連付けられていない手動で登録されたシングルトンインスタンスを指している場合があります。
BeanFactory
は、基本的に SpringのInversion of Controlコンテナ を表す抽象概念です。 Beanを内部および外部でアプリケーションに公開します。これらのBeanを検出または取得できない場合、NoSuchBeanDefinitionException
をスローします。
以下に、BeanFactory
(または関連するクラス)がBeanを見つけられない簡単な理由と、それを確実に行う方法を示します。
以下の例では
@Configuration
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
ctx.getBean(Foo.class);
}
}
class Foo {}
@Bean
メソッド、@Component
スキャン、XML定義、またはその他の方法で、Foo
型のBean定義を登録していません。したがって、BeanFactory
によって管理されるAnnotationConfigApplicationContext
には、getBean(Foo.class)
によって要求されたBeanを取得する場所が示されていません。上記のスニペットはスローします
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.example.Foo] is defined
同様に、@Autowired
依存関係を満たそうとして例外がスローされた可能性があります。例えば、
@Configuration
@ComponentScan
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
}
}
@Component
class Foo { @Autowired Bar bar; }
class Bar { }
ここでは、Foo
から@ComponentScan
までのBean定義が登録されています。しかし、SpringはBar
を何も知りません。したがって、bar
BeanインスタンスのFoo
フィールドを自動配線しようとして、対応するBeanを見つけることができません。スロー( UnsatisfiedDependencyException
内にネスト)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.example.Bar] found for dependency [com.example.Bar]:
expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
Bean定義を登録するには、複数の方法があります。
@Bean
クラスの@Configuration
メソッドまたはXML構成の<bean>
メソッド@Component
(およびそのメタ注釈、たとえば@Repository
)からXMLの@ComponentScan
または<context:component-scan ... />
までGenericApplicationContext#registerBeanDefinition
を使用して手動でBeanDefinitionRegistryPostProcessor
を使用して手動で...もっと。
期待するBeanが適切に登録されていることを確認してください。
一般的なエラーは、Beanを複数回登録することです。同じタイプの上記のオプションを組み合わせます。たとえば、私は持っているかもしれません
@Component
public class Foo {}
およびXML設定
<context:component-scan base-packages="com.example" />
<bean name="eg-different-name" class="com.example.Foo />
このような構成では、タイプFoo
の2つのBeanを登録します。1つはfoo
という名前で、もう1つはeg-different-name
という名前です。必要以上にBeanを誤って登録しないようにしてください。それが私たちを導く...
XMLと注釈ベースの構成の両方を使用している場合は、必ず一方を他方からインポートしてください。 XMLが提供する
<import resource=""/>
Javaは @ImportResource
アノテーションを提供します。
同じタイプ(またはインターフェース)に対して複数のBeanが必要な場合があります。たとえば、アプリケーションでは、MySQLインスタンスとOracleデータベースの2つのデータベースを使用できます。このような場合、それぞれへの接続を管理する2つのDataSource
Beanがあります。 (簡略化された)例では、次の
@Configuration
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
System.out.println(ctx.getBean(DataSource.class));
}
@Bean(name = "mysql")
public DataSource mysql() { return new MySQL(); }
@Bean(name = "Oracle")
public DataSource Oracle() { return new Oracle(); }
}
interface DataSource{}
class MySQL implements DataSource {}
class Oracle implements DataSource {}
投げる
Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type [com.example.DataSource] is defined:
expected single matching bean but found 2: Oracle,mysql
@Bean
メソッドを介して登録された両方のBeanが BeanFactory#getBean(Class)
の要件を満たしているためです。両方ともDataSource
を実装します。この例では、Springには2つを区別または優先順位付けするメカニズムがありません。しかし、そのようなメカニズムは存在します。
documentation および this post で説明されているように、 @Primary
(およびXMLでの同等のもの)を使用できます。この変更により
@Bean(name = "mysql")
@Primary
public DataSource mysql() { return new MySQL(); }
前のスニペットは例外をスローせず、代わりにmysql
Beanを返します。
documentation で説明されているように、@Qualifier
(およびそれに相当するXML)を使用して、Bean選択プロセスをより詳細に制御することもできます。 @Autowired
は主にタイプごとの自動配線に使用されますが、@Qualifier
を使用すると名前で自動配線できます。例えば、
@Bean(name = "mysql")
@Qualifier(value = "main")
public DataSource mysql() { return new MySQL(); }
今として注入することができます
@Qualifier("main") // or @Qualifier("mysql"), to use the bean name
private DataSource dataSource;
問題なく。 @Resource
もオプションです。
Beanを登録する方法が複数あるように、Beanに名前を付ける方法も複数あります。
このBeanの名前、または複数の場合、このBeanのエイリアス。指定しない場合、Beanの名前は注釈付きメソッドの名前になります。指定した場合、メソッド名は無視されます。
<bean>
には、 Beanの一意の識別子を表すid
属性がありますおよびname
は、(XML)idで不正な1つ以上のエイリアスを作成するために使用できます。
@Component
およびそのメタ注釈には value
この値は、自動検出されたコンポーネントの場合にSpring Beanに変換される論理コンポーネント名の提案を示している場合があります。
それが指定されていない場合、注釈付きの型(通常は小文字のラクダ型の型名)のBean名が自動的に生成されます。
@Qualifier
は、前述のように、Beanにさらにエイリアスを追加できます。
名前で自動配線する場合は、正しい名前を使用してください。
Bean定義プロファイル Beanを条件付きで登録できます。 @Profile
、具体的には、
1つ以上の指定されたプロファイルがアクティブな場合、コンポーネントは登録に適格であることを示します。
プロファイルは、
ConfigurableEnvironment.setActiveProfiles(Java.lang.String...)
を介してプログラムでアクティブにできる、またはspring.profiles.active
プロパティをJVMシステムプロパティ、環境変数、またはweb.xmlのサーブレットコンテキストパラメータとして設定することで宣言的にアクティブにできる名前付き論理グループですWebアプリケーション。プロファイルは、統合テストで@ActiveProfiles
アノテーションを使用して宣言的にアクティブ化することもできます。
spring.profiles.active
プロパティが設定されていないこの例を検討してください。
@Configuration
@ComponentScan
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
System.out.println(Arrays.toString(ctx.getEnvironment().getActiveProfiles()));
System.out.println(ctx.getBean(Foo.class));
}
}
@Profile(value = "StackOverflow")
@Component
class Foo {
}
これにより、アクティブなプロファイルは表示されず、NoSuchBeanDefinitionException
Beanに対してFoo
がスローされます。 StackOverflow
プロファイルがアクティブではなかったため、Beanは登録されませんでした。
代わりに、適切なプロファイルを登録しながらApplicationContext
を初期化すると
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("StackOverflow");
ctx.register(Example.class);
ctx.refresh();
beanは登録されており、返されるか、注入されます。
Springは AOPプロキシ を使用して高度な動作を実装します。以下に例を示します。
@Transactional
@Cacheable
@Async
および @Scheduled
これを実現するために、Springには2つのオプションがあります。
このJDKプロキシの例を使用します(proxyTargetClass
の@EnableAsync
のデフォルトのfalse
によって達成されます)
@Configuration
@EnableAsync
public class Example {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Example.class);
System.out.println(ctx.getBean(HttpClientImpl.class).getClass());
}
}
interface HttpClient {
void doGetAsync();
}
@Component
class HttpClientImpl implements HttpClient {
@Async
public void doGetAsync() {
System.out.println(Thread.currentThread());
}
}
ここで、SpringはHttpClientImpl
型のBeanを見つけようとします。この型には、@Component
という注釈が明確に付けられているため、検出されるはずです。ただし、代わりに、例外が発生します
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.example.HttpClientImpl] is defined
SpringはHttpClientImpl
Beanをラップし、Proxy
のみを実装するHttpClient
オブジェクトを介して公開しました。だからあなたはそれを取得することができます
ctx.getBean(HttpClient.class) // returns a dynamic class: com.example.$Proxy33
// or
@Autowired private HttpClient httpClient;
program to interfaces が常に推奨されます。できない場合は、CGLIBプロキシを使用するようSpringに指示できます。たとえば、 @EnableAsync
を使用すると、 proxyTargetClass
をtrue
に設定できます。同様の注釈(EnableTransactionManagement
など)には同様の属性があります。 XMLにも同等の構成オプションがあります。
ApplicationContext
階層-Spring MVCSpringでは、 ConfigurableApplicationContext#setParent(ApplicationContext)
を使用して、他のApplicationContext
インスタンスを親としてApplicationContext
インスタンスを構築できます。子コンテキストは親コンテキストのBeanにアクセスできますが、その逆は当てはまりません。 この投稿 は、特にSpring MVCでこれがいつ役立つかについて詳しく説明します。
典型的なSpring MVCアプリケーションでは、2つのコンテキストを定義します。1つはアプリケーション全体(ルート)用、もう1つは DispatcherServlet
(ルーティング、ハンドラーメソッド、コントローラー)用です。詳細はこちらから入手できます。
また、公式ドキュメント here でも非常によく説明されています。
Spring MVC設定の一般的なエラーは、XMLの@EnableWebMvc
注釈付き@Configuration
クラスまたは<mvc:annotation-driven />
クラスを使用して、ルートコンテキストでWebMVC設定を宣言することですが、 @Controller
サーブレットコンテキスト。 ルートコンテキストはサーブレットコンテキストに到達してBeanを見つけることができないため、ハンドラーは登録されず、すべてのリクエストは404で失敗します。NoSuchBeanDefinitionException
は表示されませんが、効果は同じです。
Beanが適切なコンテキストで登録されていることを確認してください。 WebMVCに登録されたBeanがそれらを見つけることができる(HandlerMapping
、HandlerAdapter
、ViewResolver
、ExceptionResolver
など)。最善の解決策は、Beanを適切に分離することです。 DispatcherServlet
はリクエストのルーティングと処理を担当するため、関連するすべてのBeanはそのコンテキストに入る必要があります。ルートコンテキストをロードするContextLoaderListener
は、アプリケーション、サービス、リポジトリなどの残りのアプリケーションが必要とするBeanを初期化する必要があります。
一部の既知のタイプのBeanは、Springによって特別な方法で処理されます。たとえば、MovieCatalog
の配列をフィールドに挿入しようとした場合
@Autowired
private MovieCatalog[] movieCatalogs;
Springは、MovieCatalog
型のすべてのBeanを検出し、それらを配列にラップして、その配列を挿入します。これは @Autowired
を議論する春のドキュメント で説明されています。同様の動作がSet
、List
、およびCollection
インジェクションターゲットに適用されます。
Map
インジェクションターゲットの場合、キータイプがString
の場合、Springもこのように動作します。たとえば、あなたが持っている場合
@Autowired
private Map<String, MovieCatalog> movies;
Springは、MovieCatalog
型のすべてのBeanを検出し、それらを値としてMap
に追加します。対応するキーはBean名になります。
前に説明したように、要求されたタイプのBeanが利用できない場合、SpringはNoSuchBeanDefinitionException
をスローします。ただし、次のようなコレクションタイプのBeanを宣言したい場合もあります。
@Bean
public List<Foo> fooList() {
return Arrays.asList(new Foo());
}
そしてそれらを注入する
@Autowired
private List<Foo> foos;
この例では、コンテキストにNoSuchBeanDefinitionException
Beanがないため、SpringはFoo
で失敗します。ただし、Foo
Beanは必要ありませんでした。List<Foo>
Beanは必要でした。 Spring 4.3以前では、@Resource
を使用する必要がありました
それ自体がコレクション/マップまたは配列タイプとして定義されているBeanの場合、
@Resource
は、特定のコレクションまたは配列Beanを一意の名前で参照する優れたソリューションです。そうは言っても、4.3の時点で、コレクション/マップおよび配列型は、要素型情報が@Autowired
戻り型シグネチャで保持されている限り、Springの@Bean
型一致アルゴリズムによっても一致できます。コレクションの継承階層。この場合、前の段落で概説したように、修飾子の値を使用して同じ型のコレクションから選択できます。
これは、コンストラクター、セッター、およびフィールド注入で機能します。
@Resource
private List<Foo> foos;
// or since 4.3
public Example(@Autowired List<Foo> foos) {}
ただし、@Bean
メソッドの場合は失敗します。
@Bean
public Bar other(List<Foo> foos) {
new Bar(foos);
}
ここでは、Springは@Resource
メソッドであるため、メソッドに注釈を付ける@Autowired
または@Bean
を無視します。したがって、ドキュメントで説明されている動作を適用できません。ただし、Spring Expression Language(SpEL)を使用して、名前でBeanを参照できます。上記の例では、使用できます
@Bean
public Bar other(@Value("#{fooList}") List<Foo> foos) {
new Bar(foos);
}
fooList
という名前のBeanを参照して挿入します。