這是Bella醬的第 50 期分享web
做者 | Giraffe面試
來源 | http://dwz.date/b4puspring
概述
org.springframework.beans.factory.NoSuchBeanDefinitionException
是很常見的異常,能夠說絕大多數使用過 Spring 的人都曾遇到過它。本文旨在總結下NoSuchBeanDefinitionException(如下簡稱 NSBDE)的含義,哪些狀況下可能拋出 NSBDE,和如何解決(文中配置均用 JavaConfig)。緩存
什麼是 NoSuchBeanDefinitionException
從字面其實就很好理解,NoSuchBeanDefinitionException 就是沒有找到指定 Bean 的 Definition。NoSuchBeanDefinitionException 的 JavaDoc是這樣定義的:微信
Exception thrown when a BeanFactory is asked for a bean instance for which it cannot find a definition. This may point to a non-existing bean, a non-unique bean, or a manually registered singleton instance without an associated bean definition.app
下面看看可能拋出 NSBDE 的一些狀況。框架
狀況1: No qualifying bean of type […] found for dependency
最多見的拋出 NSBDE 的狀況就是在一個 BeanA 中注入 BeanB 時找不到 BeanB 的定義。例子以下:異步
@Component
public class BeanA {
@Autowired
private BeanB dependency;
//...
}
當在 BeanA 中注入 BeanB 時,若是在 Spring 上下文中找不到 BeanB 的定義,就會拋出 NSBDE。異常信息以下:編輯器
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [org.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)}
拋異常的緣由在異常信息中說的很清楚:expected at least 1 bean which qualifies as autowire candidate for this dependency
。因此要麼是 BeanB 不存在在 Spring 上下文中(好比沒有標註 @ Component,@Repository,@Service, @Controller等註解) ,要麼就是 BeanB 所在的包沒有被 Spring 掃描到。分佈式
解決辦法就是先確認 BeanB 有沒有被某些註解聲明爲 Bean:
package org.baeldung.packageB;
@Component
public class BeanB { ...}
若是 BeanB 已經被聲明爲一個 Bean,就再確認 BeanB 所在的包有沒有被掃描。
@Configuration
@ComponentScan("org.baeldung.packageB")
public class ContextWithJavaConfig {
}
狀況2: No qualifying bean of type […] is defined
還有一種可能拋出 NSBDE 的狀況是在上下文中存在着兩個 Bean,好比有一個接口 IBeanB,它有兩個實現類 BeanB1 和 BeanB2。
@Component
public class BeanB1 implements IBeanB {
//
}
@Component
public class BeanB2 implements IBeanB {
//
}
如今,若是 BeanA 按照下面的方式注入,那麼 Spring 將不知道要注入兩個實現中的哪個,就會拋出 NSBDE。
@Component
public class BeanA {
@Autowired
private IBeanB dependency;
}
異常信息以下:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
No qualifying bean of type
[org.baeldung.packageB.IBeanB] is defined:
expected single matching bean but found 2: beanB1,beanB2
仔細看異常信息會發現,並非直接拋出 NSBDE,而是它的子類 NoUniqueBeanDefinitionException
,這是 Spring 3.2.1 以後引入的新異常,目的就是爲了和第一種找不到 Bean Definition 的狀況做區分。
解決辦法1就是利用 @Qualifier
註解,明確指定要注入的 Bean 的名字(BeanB2 默認的名字就是 beanB2)。
@Component
public class BeanA {
@Autowired
@Qualifier("beanB2")
private IBeanB dependency;
}
除了指定名字,咱們還能夠將其中一個 Bean 加上 @Primary
的註解,這樣會選擇加了 Primary 註解的 Bean 來注入,而不會拋異常:
@Component
@Primary
public class BeanB1 implements IBeanB {
//
}
這樣 Spring 就可以知道到底應該注入哪一個 Bean 了。
狀況3: No Bean Named […] is defined
NSBDE 還可能在從 Spring 上下文中經過名字獲取一個 Bean 時拋出。
@Component
public class BeanA implements InitializingBean {
@Autowired
private ApplicationContext context;
@Override
public void afterPropertiesSet() {
context.getBean("someBeanName");
}
}
在這種狀況中,若是找不到指定名字 Bean 的 Definition,就會拋出以下異常:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No bean named 'someBeanName' is defined
狀況4: 代理 Beans
Spring 經過 AOP 代理 實現了許多高級功能,好比:
經過 @Transactional完成 事務管理
經過 @Cacheable實現緩存
經過 @Async和 @Scheduled實現任務調度和異步執行
Spring 有兩種方式實現代理:
利用 JDK 動態代理機制 ,在運行時爲
實現了某些接口
的類動態建立一個實現了一樣接口的代理對象。使用 CGLIB,CGLIB 能夠在運行期擴展Java類與實現Java接口,也就是說當一個類沒有實現接口時,必須用 CGLIB 生成代理對象。
因此,當 Spring 上下文中的一個實現了某個接口的 Bean 經過JDK 動態代理機制被代理時,代理類並非繼承了目標類,而是實現一樣的接口。
也正由於如此,若是一個 Bean 經過接口注入時,能夠成功被注入。但若是是經過真正的類注入,那麼 Spring 將沒法找到匹配這個類的 Definition——由於代理類並無繼承這個類。
以 Spring 中比較常見的事務管理爲例,假設 ServiceA 中要注入 ServiceB,兩個 Service 均標註了 @Transactional
註解來進行事務管理,那麼下面的注入方式是不會正常 work 的。
@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{
}
-END-
更多精彩文章
1.自定義Spring bean容器瞭解一下?實戰及原理解讀
5.擼個框架考慮一下?超萬字長文,精講如何手寫分佈式事務框架
若是你喜歡本文
請長按二維碼,關注 Bella的技術輪子
轉發至 朋友圈,是對我最大的支持
喜歡就點個在看吧
本文分享自微信公衆號 - Bella的技術輪子(wheel_of_bella)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。