注解注入顧名思義就是通過(guò)注解來(lái)實(shí)現(xiàn)注入,Spring和注入相關(guān)的常見(jiàn)注解有Autowired、Resource、Qualifier、Service、Controller、Repository、Component。
Autowired是自動(dòng)注入,自動(dòng)從spring的上下文找到合適的bean來(lái)注入
Resource用來(lái)指定名稱注入
Qualifier和Autowired配合使用,指定bean的名稱
Service,Controller,Repository分別標(biāo)記類是Service層類,Controller層類,數(shù)據(jù)存儲(chǔ)層的類,spring掃描注解配置時(shí),會(huì)標(biāo)記這些類要生成bean。
Component是一種泛指,標(biāo)記類是組件,spring掃描注解配置時(shí),會(huì)標(biāo)記這些類要生成bean。
Spring對(duì)于Bean的依賴注入,支持多種注解方式:
1
2
3
4
5
6
7
8
9
|
@Resource javax.annotation JSR250 (Common Annotations for Java) @Inject javax.inject JSR330 (Dependency Injection for Java) @Autowired org.springframework.bean.factory Spring |
直觀上看起來(lái),@Autowired是Spring提供的注解,其他幾個(gè)都是JDK本身內(nèi)建的注解,Spring對(duì)這些注解也進(jìn)行了支持。但是使用起來(lái)這三者到底有什么區(qū)別呢?筆者經(jīng)過(guò)方法的測(cè)試,發(fā)現(xiàn)一些有意思的特性。
區(qū)別總結(jié)如下:
一、@Autowired有個(gè)required屬性,可以配置為false,這種情況下如果沒(méi)有找到對(duì)應(yīng)的bean是不會(huì)拋異常的。@Inject和@Resource沒(méi)有提供對(duì)應(yīng)的配置,所以必須找到否則會(huì)拋異常。
二、 @Autowired和@Inject基本是一樣的,因?yàn)閮烧叨际鞘褂肁utowiredAnnotationBeanPostProcessor來(lái)處理 依賴注入。但是@Resource是個(gè)例外,它使用的是CommonAnnotationBeanPostProcessor來(lái)處理依賴注入。當(dāng)然,兩者 都是BeanPostProcessor。
1
2
3
4
5
6
7
8
9
|
@Autowired 和 @Inject - 默認(rèn) autowired by type - 可以 通過(guò) @Qualifier 顯式指定 autowired by qualifier name。 - 如果 autowired by type 失敗(找不到或者找到多個(gè)實(shí)現(xiàn)),則退化為autowired by field name @Resource - 默認(rèn) autowired by field name - 如果 autowired by field name失敗,會(huì)退化為 autowired by type - 可以 通過(guò) @Qualifier 顯式指定 autowired by qualifier name - 如果 autowired by qualifier name失敗,會(huì)退化為 autowired by field name。但是這時(shí)候如果 autowired by field name失敗,就不會(huì)再退化為autowired by type了。 |
TIPS Qualified name VS Bean name
在Spring設(shè)計(jì)中,Qualified name并不等同于Bean name,后者必須是唯一的,但是前者類似于tag或者group的作用,對(duì)特定的bean進(jìn)行分類。可以達(dá)到getByTag(group)的效果。對(duì) 于XML配置的bean,可以通過(guò)id屬性指定bean name(如果沒(méi)有指定,默認(rèn)使用類名首字母小寫(xiě)),通過(guò)標(biāo)簽指定qualifier name:
1
2
3
4
|
<bean id= "lamborghini" class = "me.arganzheng.study.spring.autowired.Lamborghini" > <qualifier value= "luxury" /> <!-- inject any dependencies required by this bean --> </bean> |
如果是通過(guò)注解方式,那么可以通過(guò)@Qualifier注解指定qualifier name,通過(guò)@Named或者@Component(@Service,@Repository等)的value值指定bean name:
1
2
3
4
|
@Component ( "lamborghini" ) @Qualifier ( "luxury" ) public class Lamborghini implements Car { } |
或者
1
2
3
4
5
|
@Component @Named ( "lamborghini" ) @Qualifier ( "luxury" ) public class Lamborghini implements Car { } |
同樣,如果沒(méi)有指定bean name,那么Spring會(huì)默認(rèn)是用類名首字母小寫(xiě)(Lamborghini=>lamborghini)。
三、 通過(guò)Anotation注入依賴的方式在XML注入方式之前進(jìn)行。如果對(duì)同一個(gè)bean的依賴同時(shí)使用了兩種注入方式,那么XML的優(yōu)先。但是不同擔(dān)心通過(guò)Anotation注入的依賴沒(méi)法注入XML中配置的bean,依賴注入是在bean的注冊(cè)之后進(jìn)行的。
四、目前的autowired by type方式(筆者用的是3.2.3.RELEASE版本),Spring的AutowiredAnnotationBeanPostProcessor 實(shí)現(xiàn)都是有”bug”的,也就是說(shuō)@Autowired和@Inject都是有坑的(稱之為坑,不稱之為bug是因?yàn)槊菜剖枪室獾摹!#_@是來(lái)源于線上 的一個(gè)bug,也是這邊文章的寫(xiě)作原因。現(xiàn)場(chǎng)如下:
application-context.xml中有如下定義:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
<xml version= "1.0" encoding= "UTF-8" ?> <beans xmlns= "http://www.springframework.org/schema/beans" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xmlns:aop= "http://www.springframework.org/schema/aop" xmlns:context= "http://www.springframework.org/schema/context" xmlns:util= "http://www.springframework.org/schema/util" xsi:schemaLocation=" http: //www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http: //www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http: //www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http: //www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd"> <context:annotation-config /> <context:component-scan base- package = "me.arganzheng.study" /> <util:constant id= "en" static -field= "me.arganzheng.study.spring.autowired.Constants.Language.EN" /> <util:constant id= "ja" static -field= "me.arganzheng.study.spring.autowired.Constants.Language.JP" /> <util:constant id= "ind" static -field= "me.arganzheng.study.spring.autowired.Constants.Language.IND" /> <util:constant id= "pt" static -field= "me.arganzheng.study.spring.autowired.Constants.Language.PT" /> <util:constant id= "th" static -field= "me.arganzheng.study.spring.autowired.Constants.Language.TH" /> <util:constant id= "ar" static -field= "me.arganzheng.study.spring.autowired.Constants.Language.AR" /> <util:constant id= "en-rIn" static -field= "me.arganzheng.study.spring.autowired.Constants.Language.EN_RIN" /> <util:map id= "languageChangesMap" key-type= "java.lang.String" value-type= "java.lang.String" > <entry key= "pt" value= "pt" /> <entry key= "br" value= "pt" /> <entry key= "jp" value= "ja" /> <entry key= "ja" value= "ja" /> <entry key= "ind" value= "ind" /> <entry key= "id" value= "ind" /> <entry key= "en-rin" value= "en-rIn" /> <entry key= "in" value= "en-rIn" /> <entry key= "en" value= "en" /> <entry key= "gb" value= "en" /> <entry key= "th" value= "th" /> <entry key= "ar" value= "ar" /> <entry key= "eg" value= "ar" /> </util:map> </beans> |
其中static-field應(yīng)用的常量定義在如下類中:
1
2
3
4
5
6
7
8
9
10
11
12
|
package me.arganzheng.study.spring.autowired; public interface Constants { public interface Language { public static final String EN = "CommonConstants.LANG_ENGLISH" ; public static final String JP = "CommonConstants.LANG_JAPANESE" ; public static final String IND = "CommonConstants.LANG_INDONESIAN" ; public static final String PT = "CommonConstants.LANG_PORTUGUESE" ; public static final String TH = "CommonConstants.LANG_THAI" ; public static final String EN_RIN = "CommonConstants.LANG_ENGLISH_INDIA" ; public static final String AR = "CommonConstants.LANG_Arabic" ; } } |
然后如果我們?cè)诖a中如下聲明依賴:
1
2
3
4
5
6
7
8
9
10
|
public class AutowiredTest extends BaseSpringTestCase { @Autowired private Map<String, String> languageChangesMap; @Test public void testAutowired() { notNull(languageChangesMap); System.out.println(languageChangesMap.getClass().getSimpleName()); System.out.println(languageChangesMap); } } |
Guess what,詭異的事情發(fā)生了!
運(yùn)行結(jié)果如下:
1
2
|
LinkedHashMap {en=CommonConstants.LANG_ENGLISH, ja=CommonConstants.LANG_JAPANESE, ind=CommonConstants.LANG_INDONESIAN, pt=CommonConstants.LANG_PORTUGUESE, th=CommonConstants.LANG_THAI, ar=CommonConstants.LANG_Arabic, en-rIn=CommonConstants.LANG_ENGLISH_INDIA} |
也就是說(shuō)Map
嚴(yán)重: Caught exception while allowing TestExecutionListener
1
2
3
4
5
6
7
8
9
|
[org.springframework.test.context.support.DependencyInjectionTestExecutionListener @5c51ee0a ] to prepare test instance [me.arganzheng.study.spring.autowired.AutowiredTest @6e301e0 ] org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'me.arganzheng.study.spring.autowired.AutowiredTest' : Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.util.Map me.arganzheng.study.spring.autowired.AutowiredTest.languageChangesMap; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: { @org .springframework.beans.factory.annotation.Autowired(required= true )} ... ed by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: { @org .springframework.beans.factory.annotation.Autowired(required= true )} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java: 986 ) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java: 843 ) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java: 768 ) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java: 486 ) ... 28 more |
debug了一下,發(fā)現(xiàn)確實(shí)是Spring的一個(gè)bug。在DefaultListableBeanFactory的這個(gè)方法出問(wèn)題了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { ... else if (Map. class .isAssignableFrom(type) && type.isInterface()) { Class<?> keyType = descriptor.getMapKeyType(); if (keyType == null || !String. class .isAssignableFrom(keyType)) { if (descriptor.isRequired()) { throw new FatalBeanException( "Key type [" + keyType + "] of map [" + type.getName() + "] must be assignable to [java.lang.String]" ); } return null ; } Class<?> valueType = descriptor.getMapValueType(); if (valueType == null ) { if (descriptor.isRequired()) { throw new FatalBeanException( "No value type declared for map [" + type.getName() + "]" ); } return null ; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor); if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor); } return null ; } if (autowiredBeanNames != null ) { autowiredBeanNames.addAll(matchingBeans.keySet()); } return matchingBeans; } ... } |
關(guān)鍵在這一句:Map
嚴(yán)重: Caught exception while allowing TestExecutionListener
1
2
3
4
5
6
7
8
|
[org.springframework.test.context.support.DependencyInjectionTestExecutionListener @9476189 ] to prepare test instance [me.arganzheng.study.spring.autowired.AutowiredTest @2d546e21 ] ... Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: { @org .springframework.beans.factory.annotation.Autowired(required= true ), @org .springframework.beans.factory.annotation.Qualifier(value=languageChangesMap)} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java: 986 ) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java: 843 ) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java: 768 ) at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java: 486 ) ... 28 more |
debug了一下,發(fā)現(xiàn)跟沒(méi)有指定qualifie name是一樣的執(zhí)行路徑。不是指定了bean name了嗎?為什么還是autowired by type呢?仔細(xì)查看了一下才發(fā)現(xiàn)。DefaultListableBeanFactory的doResolveDependency方法對(duì)首先對(duì)類型做 了區(qū)別:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
|
protected Object doResolveDependency(DependencyDescriptor descriptor, Class<?> type, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException { Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null ) { if (value instanceof String) { String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null ); value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); return (descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } if (type.isArray()) { Class<?> componentType = type.getComponentType(); Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, descriptor); if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { raiseNoSuchBeanDefinitionException(componentType, "array of " + componentType.getName(), descriptor); } return null ; } if (autowiredBeanNames != null ) { autowiredBeanNames.addAll(matchingBeans.keySet()); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); return converter.convertIfNecessary(matchingBeans.values(), type); } else if (Collection. class .isAssignableFrom(type) && type.isInterface()) { Class<?> elementType = descriptor.getCollectionType(); if (elementType == null ) { if (descriptor.isRequired()) { throw new FatalBeanException( "No element type declared for collection [" + type.getName() + "]" ); } return null ; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, descriptor); if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { raiseNoSuchBeanDefinitionException(elementType, "collection of " + elementType.getName(), descriptor); } return null ; } if (autowiredBeanNames != null ) { autowiredBeanNames.addAll(matchingBeans.keySet()); } TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); return converter.convertIfNecessary(matchingBeans.values(), type); } else if (Map. class .isAssignableFrom(type) && type.isInterface()) { Class<?> keyType = descriptor.getMapKeyType(); if (keyType == null || !String. class .isAssignableFrom(keyType)) { if (descriptor.isRequired()) { throw new FatalBeanException( "Key type [" + keyType + "] of map [" + type.getName() + "] must be assignable to [java.lang.String]" ); } return null ; } Class<?> valueType = descriptor.getMapValueType(); if (valueType == null ) { if (descriptor.isRequired()) { throw new FatalBeanException( "No value type declared for map [" + type.getName() + "]" ); } return null ; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, descriptor); if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { raiseNoSuchBeanDefinitionException(valueType, "map with value type " + valueType.getName(), descriptor); } return null ; } if (autowiredBeanNames != null ) { autowiredBeanNames.addAll(matchingBeans.keySet()); } return matchingBeans; } else { Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (descriptor.isRequired()) { raiseNoSuchBeanDefinitionException(type, "" , descriptor); } return null ; } if (matchingBeans.size() > 1 ) { String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor); if (primaryBeanName == null ) { throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet()); } if (autowiredBeanNames != null ) { autowiredBeanNames.add(primaryBeanName); } return matchingBeans.get(primaryBeanName); } // We have exactly one match. Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); if (autowiredBeanNames != null ) { autowiredBeanNames.add(entry.getKey()); } return entry.getValue(); } } |
如果是Array,Collection或者M(jìn)ap,則根據(jù)集合類中元素的類型來(lái)進(jìn)行autowired by type(Map使用value的類型)。為什么這么特殊處理呢?原來(lái),Spring是為了達(dá)到這樣的目的:讓你可以一次注入所有符合類型的實(shí)現(xiàn),也就是 說(shuō)可以這樣子注入:
@Autowired
private List<Car> cars;
如果你的car有多個(gè)實(shí)現(xiàn),那么都會(huì)注入進(jìn)來(lái),不會(huì)再報(bào)
1
2
3
|
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [me.arganzheng.study.spring.autowired.Car] is defined: expected single matching bean but found 2 : [audi, toyota]. |
然而,上面的情況如果你用@Resource則不會(huì)有這個(gè)問(wèn)題:
1
2
3
4
5
6
7
8
9
10
11
|
public class AutowiredTest extends BaseSpringTestCase { @Resource @Qualifier ( "languageChangesMap" ) private Map<String, String> languageChangesMap; @Test public void testAutowired() { assertNotNull(languageChangesMap); System.out.println(languageChangesMap.getClass().getSimpleName()); System.out.println(languageChangesMap); } } |
正常運(yùn)行:
1
2
|
LinkedHashMap {pt=pt, br=pt, jp=ja, ja=ja, ind=ind, id=ind, en-rin=en-rIn, in=en-rIn, en=en, gb=en, th=th, ar=ar, eg=ar} |
當(dāng)然,你如果不指定@Qualifier(“languageChangesMap”),同時(shí)field name不是languageChangesMap,那么還是一樣報(bào)錯(cuò)的。
1
2
3
4
5
6
7
8
9
10
11
|
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [map with value type java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: { @javax .annotation.Resource(shareable= true , mappedName=, description=, name=, type= class java.lang.Object, authenticationType=CONTAINER, lookup=)} at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java: 986 ) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java: 843 ) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java: 768 ) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java: 438 ) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java: 416 ) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java: 550 ) at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java: 150 ) at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java: 87 ) at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessPropertyValues(CommonAnnotationBeanPostProcessor.java: 303 ) ... 26 more |
而且,@Resource也可以實(shí)現(xiàn)上面的List接收所有實(shí)現(xiàn):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public class AutowiredTest extends BaseSpringTestCase { @Resource @Qualifier ( "languageChangesMap" ) private Map<String, String> languageChangesMap; @Resource private List<Car> cars; @Test public void testAutowired() { assertNotNull(languageChangesMap); System.out.println(languageChangesMap.getClass().getSimpleName()); System.out.println(languageChangesMap); assertNotNull(cars); System.out.println(cars.getClass().getSimpleName()); System.out.println(cars); } } |
運(yùn)行的妥妥的:
1
2
3
|
LinkedHashMap {pt=pt, br=pt, jp=ja, ja=ja, ind=ind, id=ind, en-rin=en-rIn, in=en-rIn, en=en, gb=en, th=th, ar=ar, eg=ar} ArrayList |
[me.arganzheng.study.spring.autowired.Audi@579584da, me.arganzheng.study.spring.autowired.Toyota@19453122]
這是因?yàn)锧Resource注解使用的是CommonAnnotationBeanPostProcessor處理器,跟 AutowiredAnnotationBeanPostProcessor不是同一個(gè)作者[/偷笑]。這里就不分析了,感興趣的同學(xué)可以自己看代碼研究 一下。
最終結(jié)論如下:
1、@Autowired和@Inject
autowired by type 可以 通過(guò)@Qualifier 顯式指定 autowired by qualifier name(非集合類。注意:不是autowired by bean name!)
如果 autowired by type 失敗(找不到或者找到多個(gè)實(shí)現(xiàn)),則退化為autowired by field name(非集合類)
2、@Resource
默認(rèn) autowired by field name
如果 autowired by field name失敗,會(huì)退化為 autowired by type
可以 通過(guò)@Qualifier 顯式指定 autowired by qualifier name
如果 autowired by qualifier name失敗,會(huì)退化為 autowired by field name。但是這時(shí)候如果 autowired by field name失敗,就不會(huì)再退化為autowired by type了
測(cè)試工程保存在GitHub上,是標(biāo)準(zhǔn)的maven工程,感興趣的同學(xué)可以clone到本地運(yùn)行測(cè)試一下。
補(bǔ)充
有同事指出Spring官方文檔上有這么一句話跟我的結(jié)有點(diǎn)沖突:
However, although you can use this convention to refer to specific beans by name, @Autowired is fundamentally about type-driven injection with optional semantic qualifiers. This means that qualifier values, even with the bean name fallback, always have narrowing semantics within the set of type matches; they do not semantically express a reference to a unique bean id.
也就是說(shuō)@Autowired即使加了@Qualifier注解,其實(shí)也是autowired by type。@Qualifier只是一個(gè)限定詞,過(guò)濾條件而已。重新跟進(jìn)了一下代碼,發(fā)現(xiàn)確實(shí)是這樣子的。Spring設(shè)計(jì)的這個(gè) @Qualifier name 并不等同于 bean name。他有點(diǎn)類似于一個(gè)tag。不過(guò)如果這個(gè)tag是唯一的化,那么其實(shí)效果上等同于bean name。實(shí)現(xiàn)上,Spring是先getByType,得到list candicates,然后再根據(jù)qualifier name進(jìn)行過(guò)濾。
再定義一個(gè)蘭博基尼,這里使用@Qualifier指定:
1
2
3
4
5
6
7
|
package me.arganzheng.study.spring.autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; @Component @Qualifier ( "luxury" ) public class Lamborghini implements Car { } |
再定義一個(gè)勞斯萊斯,這里故意用@Named指定:
1
2
3
4
5
6
7
|
package me.arganzheng.study.spring.autowired; import javax.inject.Named; import org.springframework.stereotype.Component; @Component @Named ( "luxury" ) public class RollsRoyce implements Car { } |
測(cè)試一下注入定義的豪華車:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package me.arganzheng.study.spring.autowired; import static junit.framework.Assert.assertNotNull; import java.util.List; import me.arganzheng.study.BaseSpringTestCase; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; /** * * @author zhengzhibin * */ public class AutowiredTest extends BaseSpringTestCase { @Autowired @Qualifier ( "luxury" ) private List<Car> luxuryCars; @Test public void testAutowired() { assertNotNull(luxuryCars); System.out.println(luxuryCars.getClass().getSimpleName()); System.out.println(luxuryCars); } } |
運(yùn)行結(jié)果如下:
1
2
|
ArrayList [me.arganzheng.study.spring.autowired.Lamborghini @66b875e1 , me.arganzheng.study.spring.autowired.RollsRoyce @58433b76 ] |
補(bǔ)充:Autowiring modes
Spring支持四種autowire模式,當(dāng)使用XML配置方式時(shí),你可以通過(guò)autowire屬性指定。
1
2
3
4
|
no. (Default) No autowiring. Bean references must be defined via a ref element. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. byName. Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master, and uses it to set the property. byType. Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set. constructor. Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised. |
如果使用@Autowired、@Inject或者@Resource注解的時(shí)候,則稍微復(fù)雜一些,會(huì)有一個(gè)失敗退化過(guò)程,并且引入了Qualifier。不過(guò)基本原理是一樣。