1. 概述
本教程將討論 Spring 中的 org.springframework.beans.factory.NoSuchBeanDefinitionException 異常。
該異常通常由 BeanFactory 在嘗試解析 Spring 上下文中 未定義 bean 時拋出。
我們將探討導致此問題可能的原因以及可用的解決方案。
當然,異常會在我們意想不到的時候發生,因此請查看 Spring 中所有異常和解決方案的完整列表。
2. 原因:未找到類型[…]的合格Bean作為依賴項
導致此異常的最常見原因是嘗試注入未定義的Bean。例如,BeanB 正在注入一個協作對象,BeanA:
@Component
public class BeanA {
@Autowired
private BeanB dependency;
//...
}如果依賴 BeanB 未在 Spring 上下文中定義,引導過程將因 “未找到該 bean 定義”異常 失敗:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.baeldung.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 明確指出:該依賴項至少應有 1 個符合自動注入候選者的 Bean。
原因可能是 BeanB 在上下文中不存在——如果 Bean 通過 類路徑掃描 自動拾取,並且 BeanB 正確地被標註為 Bean(如 @Component、@Repository、@Service、@Controller 等)——則它可能定義在 未被 Spring 掃描的包中:
package com.baeldung.packageB;
@Component
public class BeanB { ...}類路徑掃描可以配置如下:
@Configuration
@ComponentScan("com.baeldung.packageA")
public class ContextWithJavaConfig {
...
}如果豆子沒有自動掃描,而是手動定義,那麼BeanB在當前Spring上下文中根本未定義。
3. 原因:字段 […] 在 […] 中需要一個類型為 […] 的 Bean,但該 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.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanA required a bean of type 'com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanB' that could not be found.
Action:
Consider defining a bean of type 'com.baeldung.springbootmvc.nosuchbeandefinitionexception.BeanB' in your configuration.以下是關於 com.baeldung.springbootmvc.nosuchbeandefinitionexception 包的信息,該包包含 BeanA、BeanB 和 NoSuchBeanDefinitionDemoApp。
4. 原因:未定義類型[…]的合格Bean
另一個導致該異常的原因是上下文中存在兩個Bean定義,而不是一個。
例如,接口 IBeanB 由兩個Bean BeanB1 和 BeanB2 實現:
@Component
public class BeanB1 implements IBeanB {
//
}
@Component
public class BeanB2 implements IBeanB {
//
}現在如果 BeanA 自動注入這個接口,Spring 將不會知道應該注入哪個實現:
@Component
public class BeanA {
@Autowired
private IBeanB dependency;
...
}再次強調,這將會導致NoSuchBeanDefinitionException由BeanFactory拋出:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type
[com.baeldung.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2同樣,Spring 明確指示了 Wiring 失敗的原因:預期只有一個匹配的 Bean,但找到了 2 個。
然而,請注意,在這種情況下拋出的異常不是 NoSuchBeanDefinitionException,而是一個子類:NoUniqueBeanDefinitionException。這個新的異常在 Spring 3.2.1 中 引入的,正是為了區分沒有找到 Bean 定義和在上下文中找到多個定義的情況。
在此變更之前,異常是上述的。
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [com.baeldung.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2解決此問題的一種方法是使用 @Qualifier 註解,明確指定我們想要注入的 Bean 的名稱。
@Component
public class BeanA {
@Autowired
@Qualifier("beanB2")
private IBeanB dependency;
...
}現在,Spring 已經擁有足夠的信息來決定注入哪個 Bean —— BeanB1 或 BeanB2 ( BeanB2 的默認名稱是 beanB2)。
5. 原因:未定義 Bean […]
當 Spring 上下文中嘗試通過名稱請求未定義的 Bean 時,可能會拋出 <em>NoSuchBeanDefinitionException</em> 異常。
@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 清楚地簡潔地表明瞭失敗的原因:未定義名為 X 的 Bean。
6. 原因:代理 Bean
當上下文中某個 Bean 使用 JDK Dynamic Proxy 機制進行代理時,代理不會繼承目標 Bean (但它會實現相同的接口)。
由於此原因,如果 Bean 通過接口注入,則會正確地進行綁定。但是,如果 Bean 通過實際的類注入,Spring 將無法找到與類匹配的 Bean 定義,因為代理實際上並沒有擴展該類。
Bean 被代理的一個非常常見的原因是 Spring 的事務支持,即那些帶有 @Transactional 註解的 Bean。
例如,如果 ServiceA 注入 ServiceB,並且這兩個服務都具有事務支持,那麼 通過類定義注入 將不起作用:
@Service
@Transactional
public class ServiceA implements IServiceA{
@Autowired
private ServiceB serviceB;
...
}
@Service
@Transactional
public class ServiceB implements IServiceB{
...
}同樣的兩個服務,這次正確地通過接口注入,也會沒問題:
@Service
@Transactional
public class ServiceA implements IServiceA{
@Autowired
private IServiceB serviceB;
...
}
@Service
@Transactional
public class ServiceB implements IServiceB{
...
}7. 結論
本文討論了常見 NoSuchBeanDefinitionException 的可能原因,重點介紹瞭如何在實踐中解決這些異常。
最後,Spring 中所有異常及其解決方案的完整列表 值得您收藏。