當(dāng)注入的 Bean 存在沖突時(shí),到底有多少種解決方案?松哥總結(jié)了五種!
當(dāng)我們從 Spring 容器中“拉”取一個(gè) Bean 回來的時(shí)候,可以按照名字去拉取,也可以按照類型去拉取,按照 BeanName 拉取的話,一般來說只要 BeanName 書寫沒有問題,都是沒問題的。但是如果是按照類型去拉取,則可能會(huì)因?yàn)?Bean 存在多個(gè)實(shí)例從而導(dǎo)致失敗。
在前面的文章中,松哥和小伙伴們分享了 @Primary、@Qualifier 注解在處理該問題時(shí)的一些具體的方案,但是都是零散的,今天咱們來把這些方案總結(jié)一下,順便再來看看是否還存在其他方案?
1. 問題呈現(xiàn)
假設(shè)我有 A、B 兩個(gè)類,在 A 中注入 B,如下:
@Component
public class A {
@Autowired
B b;
}
至于 B,則在配置類中存在多個(gè)實(shí)例:
@Configuration
@ComponentScan
public class JavaConfig {
@Bean("b1")
B b1() {
return new B();
}
@Bean("b2")
B b2() {
return new B();
}
}
這樣的項(xiàng)目啟動(dòng)之后,必然會(huì)拋出如下異常:
圖片
今天我們就來總結(jié)下這個(gè)問題的解決方案。
2. 解決方案分析
2.1 @Resource
使用 @Resource 注解,這個(gè)應(yīng)該是大家最容易想到的方案之一,不過使用 @Resource 注解需要額外添加依賴:
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>2.1.1</version>
</dependency>
加了依賴之后,現(xiàn)在就可以直接使用 @Resource 注解了:
@Service
public class A {
@Resource(name = "b1")
B b;
}
2.2 @Qualifier 指定 name
另一種方案就是搭配 @Qualifier 注解,通過該注解指定 Bean 的名稱:
@Service
public class A {
@Autowired
@Qualifier("b1")
B b;
}
2.3 @Qualifier 不指定 name
這種方案也是搭配 @Qualifier,但是并不指定 BeanName,而是在 B 注冊和 A 中注入 B 的時(shí)候,分別標(biāo)記一個(gè) @Qualifier 注解:
@Service
public class A {
@Autowired
@Qualifier
B b;
}
@Configuration
@ComponentScan
public class JavaConfig {
@Bean
@Qualifier
B b1() {
return new B();
}
@Bean
B b2() {
return new B();
}
}
2.4 不作為候選 Bean
另外還有一種方案,就是在注冊 Bean 的時(shí)候,告訴 Spring 容器,這個(gè) Bean 在通過 type 進(jìn)行注入的時(shí)候,不作為候選 Bean。
小伙伴們知道,在第一小節(jié)中報(bào)的錯(cuò),原因就是因?yàn)楦鶕?jù) type 去查找相應(yīng)的 Bean 的時(shí)候,找到了多個(gè)候選 Bean,所以才會(huì)報(bào)錯(cuò),所以我們注冊一個(gè) Bean 的時(shí)候,可以設(shè)置該 Bean 不是候選 Bean,這個(gè)設(shè)置并不影響通過 name 注入一個(gè) Bean。
具體配置如下:
Java 代碼配置:
@Configuration
@ComponentScan
public class JavaConfig {
@Bean(autowireCandidate = false)
B b1() {
return new B();
}
@Bean
B b2() {
return new B();
}
}
autowireCandidate 屬性就表示這個(gè) Bean 不是一個(gè)候選 Bean。
XML 配置:
<bean class="org.javaboy.bean.p2.B" autowire-candidate="false"/>
autowire-candidate 屬性表示當(dāng)前 Bean 是否作為一個(gè)候選 Bean。
2.5 @Primary
差點(diǎn)把我們最常用的方案忘了。@Primary 表示當(dāng)通過 type 注入的時(shí)候,如果當(dāng)前 Bean 存在多個(gè)實(shí)例,則優(yōu)先使用帶有 @Primary 注解的 Bean。
@Service
public class A {
@Autowired
B b;
}
@Configuration
@ComponentScan
public class JavaConfig {
@Bean
@Primary
B b1() {
return new B();
}
@Bean
B b2() {
return new B();
}
}
好啦,這就是松哥總結(jié)出來的 5 種方案,實(shí)際上,基于這五種,還能衍生出來一些方案,這就需要小伙伴們自行探索啦~
最后大家思考這樣一問題:對于第一小節(jié)提出來的問題,如果同時(shí)使用 2.2 和 2.5 小節(jié)的方案,那么哪一個(gè)會(huì)生效呢?