本文翻譯自baeldung的文章《Wiring in Spring: @Autowired, @Resource and @Inject》,文中的引用和括號(hào)內(nèi)的斜線內(nèi)容為譯者自己的理解和感悟麦向,僅供參考
1. 概述
這篇關(guān)于Spring Framework的文章將為你演示三種與依賴注入相關(guān)的注解的使用瘟裸,它們分別是:@Resource, @Inject, 和 @Autowired。這些注解為你的類提供了通過聲明解決依賴的方式诵竭,舉個(gè)例子:
@Autowired
ArbitraryClass arbObject;
而不是通過直接實(shí)例化對(duì)象的方式(通過顯式調(diào)用構(gòu)造方法)话告,比如:
ArbitraryClass arbObject = new ArbitraryClass();
三種注解的其中兩個(gè)分別屬于Java的擴(kuò)展包:javax.annotation.Resource
和 javax.inject.Inject
。(Java官方的擴(kuò)展包)而@Autowired
注解來自于org.springframework.beans.factory.annotation 包卵慰。(spring框架提供的注解包)
三種注解都可以通過字段注入或setter方法注入的形式來解決依賴問題沙郭。在下面這個(gè)簡(jiǎn)單而實(shí)用的例子中,將基于三種注解各自的執(zhí)行方式來演示它們使用上的區(qū)別裳朋。
例子將借助集成測(cè)試專注于展示如何使用這三種注解病线,在測(cè)試中的依賴關(guān)系可以是任意的文件或任意的類。
文章示例中展示的僅為部分源碼鲤嫡,可在文末點(diǎn)擊GitHub鏈接前往查看和下載完整源碼
2. @Resource注解
@Resource注解是JSR-250中定義的注解集中的一員送挑,并且歸入了Java EE的規(guī)范包中。
JSR是由JCP(Java Community Process)定制的Java標(biāo)準(zhǔn)化技術(shù)規(guī)范泛范,可以看做是官方給開發(fā)者提供的接口標(biāo)準(zhǔn)让虐,很多廠商會(huì)對(duì)此進(jìn)行實(shí)現(xiàn)紊撕,但也有很多會(huì)定制自己的規(guī)范罢荡。
該注解的執(zhí)行過程的優(yōu)先級(jí)順序如下:
- 命名匹配(Match by Name)
- 類型匹配(Match by Type)
- 限定符匹配(Match by Qualifier)
這些執(zhí)行過程均可以適用于setter方法注入和字段注入。
2.1 字段注入
字段注入來解決依賴是通過在實(shí)例字段上使用@Resource注解的方式進(jìn)行的对扶。
2.1.1 命名匹配
下面的集成測(cè)試案例演示了如何通過命名匹配的形式進(jìn)行字段注入:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceNameType.class)
public class FieldResourceInjectionTest {
@Resource(name="namedFile")
private File defaultFile;
@Test
public void givenResourceAnnotation_WhenOnField_ThenDependencyValid(){
assertNotNull(defaultFile);
assertEquals("namedFile.txt", defaultFile.getName());
}
}
我們一起瀏覽這段代碼区赵。在FieldResourceInjectionTest
單元測(cè)試類中的第7行,在@Resource注解的屬性中傳入了bean的名稱實(shí)現(xiàn)了通過命名來解決依賴:
@Resource(name="namedFile")
private File defaultFile;
這個(gè)配置將使用“命名匹配”規(guī)則來控制依賴關(guān)系浪南。被命名為namedFile的bean必須定義在ApplicationContextTestResourceNameType
這個(gè)應(yīng)用容器中笼才。(因?yàn)槭纠械募蓽y(cè)試指定加載了該容器類)
請(qǐng)注意,bean的id(命名)必須和對(duì)應(yīng)引用的屬性值相匹配**:
@Configuration
public class ApplicationContextTestResourceNameType {
@Bean(name="namedFile")
public File namedFile() {
File namedFile = new File("namedFile.txt");
return namedFile;
}
}
在容器中錯(cuò)誤地定義bean將會(huì)導(dǎo)致一個(gè)org.springframework.beans.factory.NoSuchBeanDefinitionException
異常的拋出络凿。在ApplicationContextTestResourceNameType應(yīng)用容器中的@Bean注里改變屬性值骡送,或者在FieldResourceInjectionTest集成測(cè)試類的@Resource注解里改變屬性值,將很容易證明出這一點(diǎn)絮记。
2.1.2 類型匹配
想演示通過類型匹配的執(zhí)行方式摔踱,只需要移除在FieldResourceInjectionTest 集成測(cè)試類里第7行的屬性,就像下面這樣:
@Resource
private File defaultFile;
然后再次運(yùn)行它怨愤。
這個(gè)測(cè)試依然將會(huì)通過派敷,因?yàn)楫?dāng)@Resource注解沒有接受到屬性值傳入的指定bean名稱時(shí),Spring Framework將會(huì)通過下一優(yōu)先級(jí)即類型匹配的方式來解決依賴注入。
2.1.3 限定符匹配
下面要演示限定符匹配的執(zhí)行方式篮愉,那么原來的集成測(cè)試場(chǎng)景將要做一些改變:定義兩個(gè)bean在ApplicationContextTestResourceQualifier應(yīng)用容器中:
@Configuration
public class ApplicationContextTestResourceQualifier {
@Bean(name="defaultFile")
public File defaultFile() {
File defaultFile = new File("defaultFile.txt");
return defaultFile;
}
@Bean(name="namedFile")
public File namedFile() {
File namedFile = new File("namedFile.txt");
return namedFile;
}
}
QualifierResourceInjectionTest集成測(cè)試類將演示限定符匹配的依賴處理過程腐芍。在這個(gè)場(chǎng)景中,需要向每個(gè)引用變量注入特定的bean依賴關(guān)系试躏。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceQualifier.class)
public class QualifierResourceInjectionTest {
@Resource
private File dependency1;
@Resource
private File dependency2;
@Test
public void givenResourceAnnotation_WhenField_ThenDependency1Valid(){
assertNotNull(dependency1);
assertEquals("defaultFile.txt", dependency1.getName());
}
@Test
public void givenResourceQualifier_WhenField_ThenDependency2Valid(){
assertNotNull(dependency2);
assertEquals("namedFile.txt", dependency2.getName());
}
}
運(yùn)行這個(gè)集成測(cè)試猪勇,將會(huì)有一個(gè)org.springframework.beans.factory.NoUniqueBeanDefinitionException
異常拋出。這個(gè)異常是由于應(yīng)用容器發(fā)現(xiàn)了兩個(gè)類型為File的bean的定義颠蕴,因而無法確定哪個(gè)bean應(yīng)該被用來解決依賴埠对。
要解決這個(gè)問題,請(qǐng)回到QualifierResourceInjectionTest單元測(cè)試類的第7到第10行:
@Resource
private File dependency1;
@Resource
private File dependency2;
然后添加以下兩行代碼:
@Qualifier("defaultFile")
@Qualifier("namedFile")
然后代碼塊將會(huì)像下面這樣:
@Resource
@Qualifier("defaultFile")
private File dependency1;
@Resource
@Qualifier("namedFile")
private File dependency2;
再次運(yùn)行一輪集成測(cè)試將會(huì)順利通過裁替。這個(gè)測(cè)試很客觀地演示了當(dāng)多個(gè)bean在容器中定義時(shí)项玛,@Qualifier注解通過指定依賴注入bean清除了未明確的困惑。
2.2 Setter方法注入
在字段上注入依賴項(xiàng)執(zhí)行過程適用于使用基于setter方法的注入弱判。
2.2.1 命名匹配
與上面的例子的區(qū)別是襟沮,MethodResourceInjectionTest集成測(cè)試中有一個(gè)setter方法:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceNameType.class)
public class MethodResourceInjectionTest {
private File defaultFile;
@Resource(name="namedFile")
protected void setDefaultFile(File defaultFile) {
this.defaultFile = defaultFile;
}
@Test
public void givenResourceAnnotation_WhenSetter_ThenDependencyValid(){
assertNotNull(defaultFile);
assertEquals("namedFile.txt", defaultFile.getName());
}
}
通過setter方法注入來解決依賴關(guān)系是通過在引用變量的相應(yīng)setter方法上增加注解來完成的。將bean依賴項(xiàng)的名稱作為屬性值傳遞給@Resource注解:
private File defaultFile;
@Resource(name="namedFile")
protected void setDefaultFile(File defaultFile) {
this.defaultFile = defaultFile;
}
在這個(gè)例子中將再次使用namedFile這個(gè)bean依賴昌腰。bean的名稱和對(duì)應(yīng)的屬性值必須匹配开伏。
按原樣再次運(yùn)行集成測(cè)試,它將順利通過遭商。
想要看到依賴關(guān)系確實(shí)是通過按名稱匹配的方式處理的固灵,可以修改@Resource注解的屬性值為你選擇的值,然后再次運(yùn)行測(cè)試劫流。這一次巫玻,測(cè)試將會(huì)拋出NoSuchBeanDefinitionException而失敗。
2.2.2 類型匹配
為了演示基于setter方法的類型匹配執(zhí)行過程祠汇,我們將使用MethodByTypeResourceTest集成測(cè)試:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceNameType.class)
public class MethodByTypeResourceTest {
private File defaultFile;
@Resource
protected void setDefaultFile(File defaultFile) {
this.defaultFile = defaultFile;
}
@Test
public void givenResourceAnnotation_WhenSetter_ThenValidDependency(){
assertNotNull(defaultFile);
assertEquals("namedFile.txt", defaultFile.getName());
}
}
按原樣運(yùn)行測(cè)試仍秤,它將順利通過。
為了驗(yàn)證File依賴項(xiàng)是否確實(shí)由類型匹配的執(zhí)行過程解析可很,將defaultFile變量的類的類型修改為另一個(gè)類類型诗力,例如String。再次執(zhí)行MethodByTypeResourceTest集成測(cè)試我抠,這次將拋出NoSuchBeanDefinitionException苇本。
這個(gè)異常異常驗(yàn)證了該過程確實(shí)使用了類型匹配的規(guī)則來解析File依賴項(xiàng)。NoSuchBeanDefinitionException異常的拋出確認(rèn)了引用變量的名稱不需要匹配bean的名字菜拓。與之相反瓣窄,依賴項(xiàng)解析依賴是通過bean的類型與引用變量的類型進(jìn)行匹配。
2.2.3 限定符匹配
下面這個(gè)MethodByQualifierResourceTest集成測(cè)試將被用來演示限定符匹配的執(zhí)行過程:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestResourceQualifier.class)
public class MethodByQualifierResourceTest {
private File arbDependency;
private File anotherArbDependency;
@Test
public void givenResourceQualifier_WhenSetter_ThenValidDependencies(){
assertNotNull(arbDependency);
assertEquals("namedFile.txt", arbDependency.getName());
assertNotNull(anotherArbDependency);
assertEquals("defaultFile.txt", anotherArbDependency.getName());
}
@Resource
@Qualifier("namedFile")
public void setArbDependency(File arbDependency) {
this.arbDependency = arbDependency;
}
@Resource
@Qualifier("defaultFile")
public void setAnotherArbDependency(File anotherArbDependency) {
this.anotherArbDependency = anotherArbDependency;
}
}
這個(gè)測(cè)試的目的是演示即使在應(yīng)用容器中定義了特定類型的多個(gè)bean實(shí)現(xiàn)尘惧,也可以通過@Qualifier注解與@Resource注解一起使用來解析依賴關(guān)系康栈。
與基于字段注入的依賴項(xiàng)處理類似,如果在應(yīng)用容器中定義了多個(gè)bean,而沒有使用@Qualifier注解指定應(yīng)使用哪個(gè)bean來注入依賴項(xiàng)啥么,那么將會(huì)拋出NoUniqueBeanDefinitionException異常登舞。
3. @Inject注解
@Inject注解歸屬于JSR-330注解集。這個(gè)注解具有以下按優(yōu)先級(jí)的執(zhí)行過程:
- 類型匹配(Match by Type)
- 限定符匹配(Match by Qualifier)
- 命名匹配(Match by Name)
這些執(zhí)行過程適用于Setter方法注入和字段注入悬荣。為了使用@Inject
注解菠秒,需要將javax.inject
庫通過Gradle或Maven進(jìn)行依賴包引入。
使用Gradle:
testCompile group: 'javax.inject', name: 'javax.inject', version: '1'
使用Maven:
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
3.1 字段注入
3.1.1 類型匹配
集成測(cè)試中的示例將被修改為使用其它類型的依賴項(xiàng)氯迂,即ArbitraryDependency類践叠。該依賴項(xiàng)只是作為一個(gè)簡(jiǎn)單依賴類,并且不具備更多的意義嚼蚀。代碼如下:
@Component
public class ArbitraryDependency {
private final String label = "Arbitrary Dependency";
public String toString() {
return label;
}
}
這一例子中的集成測(cè)試如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectType.class)
public class FieldInjectTest {
@Inject
private ArbitraryDependency fieldInjectDependency;
@Test
public void givenInjectAnnotation_WhenOnField_ThenValidDependency(){
assertNotNull(fieldInjectDependency);
assertEquals("Arbitrary Dependency",
fieldInjectDependency.toString());
}
}
不像@Resource注解優(yōu)先使用命名匹配規(guī)則禁灼,@Inject注解的默認(rèn)行為是優(yōu)先使用類型匹配規(guī)則來解決依賴。
這意味著即使一個(gè)類型依賴變量的命名與對(duì)容器中對(duì)應(yīng)定義著的bean命名不同轿曙,依賴依然會(huì)被正常處理弄捕。
成員變量的命名如下:
@Inject
private ArbitraryDependency fieldInjectDependency;
與容器中配置的bean的命名有所不同:
@Bean
public ArbitraryDependency injectDependency() {
ArbitraryDependency injectDependency = new ArbitraryDependency();
return injectDependency;
}
當(dāng)測(cè)試被運(yùn)行時(shí),這個(gè)依賴是可以被正常處理的导帝。
3.1.2 限定符匹配
如果一個(gè)特別的類有多個(gè)不同的實(shí)現(xiàn)并且一個(gè)確定的類需要一個(gè)指定的bean時(shí)守谓,又該如何處理呢?下面我們修改集成測(cè)試的例子來指定另一個(gè)依賴您单。
在下面的例子中斋荞,我們通過繼承在“類型匹配”例子中使用的ArbitraryDependency類創(chuàng)建了AnotherArbitraryDependency子類:
public class AnotherArbitraryDependency extends ArbitraryDependency {
private final String label = "Another Arbitrary Dependency";
public String toString() {
return label;
}
}
測(cè)試用例的每一項(xiàng)都是為了確保每個(gè)依賴項(xiàng)都正確地注入到每個(gè)引用變量中:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
用于演示按限定符匹配的FieldQualifierInjectTest集成測(cè)試如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectQualifier.class)
public class FieldQualifierInjectTest {
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
@Test
public void givenInjectQualifier_WhenOnField_ThenDefaultFileValid(){
assertNotNull(defaultDependency);
assertEquals("Arbitrary Dependency",
defaultDependency.toString());
}
@Test
public void givenInjectQualifier_WhenOnField_ThenNamedFileValid(){
assertNotNull(defaultDependency);
assertEquals("Another Arbitrary Dependency",
namedDependency.toString());
}
}
如果應(yīng)用容器中存在特定類的多個(gè)實(shí)現(xiàn),并且FieldQualifierInjectTest集成測(cè)試嘗試以下面列出的方式去注入依賴:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
那么將會(huì)拋出一個(gè)NoUniqueBeanDefinitionException異常虐秦。
拋出此異常是因?yàn)镾pring Framework指出某個(gè)類具有有多個(gè)實(shí)現(xiàn)平酿,并且對(duì)于使用哪個(gè)類產(chǎn)生困惑。為了闡明這種混淆的關(guān)系羡疗,請(qǐng)轉(zhuǎn)到FieldQualifierInjectTest集成測(cè)試的第7行和第10行:
@Inject
private ArbitraryDependency defaultDependency;
@Inject
private ArbitraryDependency namedDependency;
將所需要的bean名稱傳遞給@Qualifier注解染服,該注解與@Inject注解一起使用。代碼現(xiàn)在看起來像下面這樣:
@Inject
@Qualifier("defaultFile")
private ArbitraryDependency defaultDependency;
@Inject
@Qualifier("namedFile")
private ArbitraryDependency namedDependency;
@Qualifier注解在接收bean名稱時(shí)需要進(jìn)行嚴(yán)格匹配叨恨,以確保bean名稱被正確地傳遞給Qualifier,否則將會(huì)拋出NoUniqueBeanDefinitionException異常挖垛。再次運(yùn)行測(cè)試將會(huì)順利通過痒钝。
3.1.3 按名稱匹配
用于演示按名稱匹配的FieldByNameInjectTest集成測(cè)試類似于按類型匹配的方式。唯一的區(qū)別在于需要特定名稱的bean而不是特定的類型痢毒。在這個(gè)例子中送矩,我們?cè)俅卫^承ArbitraryDependency以創(chuàng)建YetAnotherArbitraryDependency子類:
public class YetAnotherArbitraryDependency extends ArbitraryDependency {
private final String label = "Yet Another Arbitrary Dependency";
public String toString() {
return label;
}
}
下面演示按名稱匹配的注入方式,我們使用以下集成測(cè)試:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestInjectName.class)
public class FieldByNameInjectTest {
@Inject
@Named("yetAnotherFieldInjectDependency")
private ArbitraryDependency yetAnotherFieldInjectDependency;
@Test
public void givenInjectQualifier_WhenSetOnField_ThenDependencyValid(){
assertNotNull(yetAnotherFieldInjectDependency);
assertEquals("Yet Another Arbitrary Dependency",
yetAnotherFieldInjectDependency.toString());
}
}
應(yīng)用容器內(nèi)容如下:
@Configuration
public class ApplicationContextTestInjectName {
@Bean
public ArbitraryDependency yetAnotherFieldInjectDependency() {
ArbitraryDependency yetAnotherFieldInjectDependency =
new YetAnotherArbitraryDependency();
return yetAnotherFieldInjectDependency;
}
}
直接運(yùn)行集成測(cè)試哪替,它將順利通過栋荸。
為了驗(yàn)證依賴是否確實(shí)是按名稱匹配的方式進(jìn)行注入,可以傳遞給@Named不同于現(xiàn)在的名稱。再次運(yùn)行測(cè)試晌块,這次將會(huì)拋出NoSuchBeanDefinitionException異常爱沟。
以上例子可以看出@Named與@Qualifier注解的使用方式和作用基本相同,都是通過bean名稱來指定依賴匆背,區(qū)別在于@Named注解與@Inject注解同屬于JSR-330注解集呼伸,而@Qualifier是Spring Framework提供的注解(
javax.inject
包中同樣提供了一個(gè)名為@Qualifier的注解,但該注解的作用是用來標(biāo)識(shí)作為限定符注解钝尸,@Named注解即為官方的一個(gè)標(biāo)準(zhǔn)實(shí)現(xiàn))括享。使用的取舍在于你對(duì)Spring Framework依賴程度的期望。
3.2 Setter方法注入
@Inject注解的setter方法注入與@Resource注解相類似珍促。不再注釋引用變量铃辖,而是注釋相應(yīng)的setter方法≈硇穑基于字段的依賴注入方式也同樣適用于setter方法注入澳叉。
4. @Autowired 注解
@Autowired注解的注入過程類似于@Inject注釋。唯一的區(qū)別是@Autowired注解是Spring框架的一部分沐悦。
Spring 的官方文檔同樣也說明了這一點(diǎn)成洗,@Autowired與@Inject注解具有相同的依賴處理過程。
此注解與@Inject注解具有相同的注入過程優(yōu)先級(jí):
- 類型匹配(Match by Type)
- 限定符匹配(Match by Qualifier)
- 命名匹配(Match by Name)
這些注入過程同樣適用于setter方法注入和字段注入藏否。
4.1 字段注入
4.1.1 類型匹配
演示@Autowired注解按類型匹配的示例與演示@Inject注解按類型匹配的示例相類似瓶殃。下面這個(gè)FieldAutowiredTest集成測(cè)試將用來演示@Autowired注解進(jìn)行類型匹配的過程:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestAutowiredType.class)
public class FieldAutowiredTest {
@Autowired
private ArbitraryDependency fieldDependency;
@Test
public void givenAutowired_WhenSetOnField_ThenDependencyResolved() {
assertNotNull(fieldDependency);
assertEquals("Arbitrary Dependency", fieldDependency.toString());
}
}
集成測(cè)試的應(yīng)用容器定義如下:
@Configuration
public class ApplicationContextTestAutowiredType {
@Bean
public ArbitraryDependency autowiredFieldDependency() {
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
}
}
這個(gè)集成測(cè)試的目的是為了證明類型匹配相比其他匹配規(guī)則具有最高的優(yōu)先級(jí)。注意FieldAutowiredTest類中的第8行的成員變量命名:
@Autowired
private ArbitraryDependency fieldDependency;
它與應(yīng)用容器中的bean名稱是不同的:
@Bean
public ArbitraryDependency autowiredFieldDependency() {
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
}
而這個(gè)測(cè)試將順利運(yùn)行通過副签。
為了確認(rèn)通過按類型匹配過程確實(shí)注入了依賴遥椿,我們修改fieldDependency成員變量的類型并再次運(yùn)行測(cè)試。這一輪FieldAutowiredTest測(cè)試必定會(huì)失敗淆储,并拋出NoSuchBeanDefinitionException異常冠场。這驗(yàn)證了按類型匹配確實(shí)處理了依賴關(guān)系。
4.1.2 限定符匹配
如果遇到了應(yīng)用容器中定義了多個(gè)實(shí)現(xiàn)bean的情況:
@Configuration
public class ApplicationContextTestAutowiredQualifier {
@Bean
public ArbitraryDependency autowiredFieldDependency() {
ArbitraryDependency autowiredFieldDependency =
new ArbitraryDependency();
return autowiredFieldDependency;
}
@Bean
public ArbitraryDependency anotherAutowiredFieldDependency() {
ArbitraryDependency anotherAutowiredFieldDependency =
new AnotherArbitraryDependency();
return anotherAutowiredFieldDependency;
}
}
再執(zhí)行下面的集成測(cè)試:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestAutowiredQualifier.class)
public class FieldQualifierAutowiredTest {
@Autowired
private ArbitraryDependency fieldDependency1;
@Autowired
private ArbitraryDependency fieldDependency2;
@Test
public void givenAutowiredQualifier_WhenOnField_ThenDep1Valid(){
assertNotNull(fieldDependency1);
assertEquals("Arbitrary Dependency", fieldDependency1.toString());
}
@Test
public void givenAutowiredQualifier_WhenOnField_ThenDep2Valid(){
assertNotNull(fieldDependency2);
assertEquals("Another Arbitrary Dependency",
fieldDependency2.toString());
}
}
將會(huì)拋出NoUniqueBeanDefinitionException
這個(gè)異常是由容器中定義了兩個(gè)bean引起的歧義導(dǎo)致的本砰。Spring Framework容器不知道哪個(gè)bean依賴應(yīng)該注入到哪個(gè)引用變量上碴裙。通過將@Qualifier注解添加到FieldQualifierAutowiredTest中的第7行和第10行將解決該問題:
@Autowired
private FieldDependency fieldDependency1;
@Autowired
private FieldDependency fieldDependency2;
代碼塊看起來將會(huì)像下面這樣:
@Autowired
@Qualifier("autowiredFieldDependency")
private FieldDependency fieldDependency1;
@Autowired
@Qualifier("anotherAutowiredFieldDependency")
private FieldDependency fieldDependency2;
再次運(yùn)行將順利通過測(cè)試。
4.1.3 按名稱匹配
我們使用相同的集成測(cè)試場(chǎng)景來演示@Autowired注解如何通過名稱匹配注入字段依賴關(guān)系点额。當(dāng)通過名稱自動(dòng)注入時(shí)舔株,需要在ApplicationContextTestAutowiredName應(yīng)用容器上使用@ComponentScan注解:
@Configuration
@ComponentScan(basePackages={"com.baeldung.dependency"})
public class ApplicationContextTestAutowiredName {
}
@ComponentScan用來標(biāo)注在哪些包中搜索帶@Component注解的Java類。例如还棱,應(yīng)用程序?qū)呙?em>com.baeldung.dependency包中使用@Component注解的類载慈。在現(xiàn)在的場(chǎng)景中,ArbitraryDependency 類由于使用了@Component注解珍手,它將會(huì)被Spring Framework檢查到:
@Component(value="autowiredFieldDependency")
public class ArbitraryDependency {
private final String label = "Arbitrary Dependency";
public String toString() {
return label;
}
}
賦給@Component注解的value屬性值autowiredFieldDependency將告訴Spring Framework老虫,ArbitraryDependency類是一個(gè)名為autowiredFieldDependency的Bean。為了使@Autowired按名稱解析依賴露懒,組件名稱必須與FieldAutowiredNameTest測(cè)試中定義的字段名稱相對(duì)應(yīng);參考第8行:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
loader=AnnotationConfigContextLoader.class,
classes=ApplicationContextTestAutowiredName.class)
public class FieldAutowiredNameTest {
@Autowired
private ArbitraryDependency autowiredFieldDependency;
@Test
public void givenAutowiredAnnotation_WhenOnField_ThenDepValid(){
assertNotNull(autowiredFieldDependency);
assertEquals("Arbitrary Dependency",
autowiredFieldDependency.toString());
}
}
運(yùn)行集成測(cè)試秤茅,它將順利通過。
但是我們?cè)趺粗?em>@Autowired注解確實(shí)使用了按名稱匹配的方式呢晒杈?將成員變量autowiredFieldDependency的名稱更改為另一個(gè)名稱嫂伞,再次運(yùn)行測(cè)試。
這一次拯钻,測(cè)試將執(zhí)行失敗并拋出NoUniqueBeanDefinitionException帖努。類似的檢查方式是將@Component的value值autowiredFieldDependency修改為另一個(gè)值并再次運(yùn)行測(cè)試。同樣將拋出NoUniqueBeanDefinitionException粪般。
這個(gè)異常證明了如果使用了錯(cuò)誤的bean名稱拼余,將不會(huì)找到有效的bean。因此亩歹, @Autowired注解使用了按名稱匹配的方式匙监。
4.2 Setter方法注入
@Autowired注解的setter方法注入與基于@Resource注解的注入方式類似⌒∽鳎基于字段的注入過程同樣適用于基于setter方法注入亭姥。
5.應(yīng)用這些注解
現(xiàn)在出現(xiàn)了一個(gè)問題,應(yīng)該在什么情況下應(yīng)用這些注解顾稀?問題的答案在于應(yīng)用程序所使用的設(shè)計(jì)方案以及開發(fā)者期望如何基于注解不同的注入方式來利用多態(tài)性达罗。
5.1 通過多態(tài)在應(yīng)用程序范圍內(nèi)使用單例
如果使用多態(tài)只是基于接口和抽象類來實(shí)現(xiàn)應(yīng)用程序行為,并且這些行為在整個(gè)應(yīng)用程序中使用静秆,則應(yīng)該使用@Inject 或 @Autowired注解粮揉。
這種方法的好處將在應(yīng)用程序升級(jí)或需要補(bǔ)丁來修復(fù)錯(cuò)誤時(shí)體現(xiàn)出來;其次在對(duì)類進(jìn)行更換時(shí)可以對(duì)應(yīng)用程序造成最小的負(fù)面影響抚笔。在此方案中扶认,處理依賴的主要方式是按類型匹配;
5.2 通過多態(tài)細(xì)粒度配置應(yīng)用程序行為
如果多態(tài)的使用是為了使應(yīng)用程序擁有更復(fù)雜的行為——每一個(gè)行為都基于不同的接口或抽象類殊橙,并且使用它們的實(shí)現(xiàn)會(huì)因應(yīng)用程序而異辐宾,則使用@Resource注解。在此方案中蛀柴,處理依賴的主要方式是按名稱匹配螃概。
5.3 在Java EE平臺(tái)處理依賴注入
如果選擇在JavaEE平臺(tái)而不是Spring處理所有的依賴,那么可以在@Resource注解和@Inject注解中選擇其一鸽疾。你應(yīng)該基于它們的默認(rèn)處理方式縮小兩種注解之間的最終使用范圍。
5.4 在Spring框架中處理依賴注入
如果應(yīng)用僅僅依賴于Spring框架训貌,那唯一選擇就是@Autowired注解制肮。
5.5 總結(jié)
下表對(duì)以上討論進(jìn)行了總結(jié):
場(chǎng)景 | @Resource | @Inject | @Autowired |
---|---|---|---|
通過多態(tài)在應(yīng)用程序范圍使用單例 | × | √ | √ |
通過多態(tài)細(xì)粒度配置應(yīng)用程序行為 | √ | × | × |
依賴注入僅由使用Java EE平臺(tái) | √ | √ | × |
依賴注入僅使用Spring Framework | × | × | √ |
6. 結(jié)論
本文旨在更深入地了解每個(gè)注解的處理過程冒窍。了解每個(gè)注解的注入規(guī)則將有助于更好地進(jìn)行整體應(yīng)用的設(shè)計(jì)和維護(hù)。
文中使用的代碼均可以在GitHub中找到豺鼻。