我知道在Spring中,能夠用@autowired @resource @Inject三種方式進行依賴注入,三種方式的區別在什麼地方呢?java
若是沒有興趣能夠直接跳到下面的結論。spring
若是不知道如何測試Spring項目的能夠點擊這裏。ide
首先咱們定義一個Person接口:測試
package site.abely.service; public interface Person { void sayHello(); }
而後定義兩個實現類Chinese,Americanui
package site.abely.service; import org.springframework.stereotype.Component; @Component public class Chinese implements Person { @Override public void sayHello() { System.out.println("你好"); } }
package site.abely.service; import org.springframework.stereotype.Component; @Component public class American implements Person { @Override public void sayHello() { System.out.println("Hello"); } }
先按類型注入,而後按照名字注入,都沒法找到惟一的一個實現類出錯。.net
咱們將American類中@Component註釋,這樣在Spring環境中只有Chinese一個實現類,測試代碼以下:指針
@Autowired Person person; @Test public void testSay(){ person.sayHello(); }
輸出你好
。code
由於只有Chinese實現了Person,因此會正確運行(經過類型查找)。對象
咱們取消American類中@Component的註釋,運行程序會出現異常:接口
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'site.abely.service.Person' available: expected single matching bean but found 2: american,chinese
由於有American和Chinese兩個實現類,Spring不知道該用哪個注入。
修改測試代碼:
@Resource Person chinese; @Test public void testSay(){ chinese.sayHello(); }
結果會輸出你好
。此時變量名chinese等於Chinese默認的Qualifier名字。
若是隻有一個實現類Chinese(刪除American類),並且Chinese不實現Person接口,此時怎麼注入Person chinese都會出錯(請求的是Person對象,注入的卻不是)。
@Autowired(required=false)中若是設置required爲false(默認爲true),則注入失敗時不會拋出異常,但person.sayHello();
調用時會出現空指針異常。
在Spring的環境下,@Inject和@Autowired是相同的,都是使用AutowiredAnnotationBeanPostProcessor來處理依賴注入,@Inject是jsr-330定義的規範,仍是比較推薦使用這種方式進行依賴注入,若是使用這種方式,切換到Guice也是能夠的。
若是硬要說兩個的區別,首先@Inject是Java EE包裏的,在SE環境須要單獨引入。另外一個區別在於@Autowired能夠設置required=false而@Inject並無這個設置選項。
先按名字注入,在按類型注入,都沒法找到惟一的一個出現異常。
這是jsr-250定義的規範,相對於jsr-330算是比較老的了。這裏不推薦使用這種注入方式,下面討論一下其注入的問題。
首先咱們註釋American裏的@Component,這樣在Spring託管的Bean裏只有Chinese實現了Person接口,測試用例以下:
@Resource Person person; @Test public void testSay(){ person.sayHello(); }
輸出結果:你好
。 此時@Resource先按名字person,並未找到person的bean,而後按照類型來找,只有Chinese,注入成功。
取消American中的@Component註釋,出現以下異常:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'site.abely.service.Person' available: expected single matching bean but found 2: american,chinese
此時不管經過名字沒法肯定,而後經過類型仍是沒法肯定,拋出異常。
修改測試代碼
@Resource Person chinese; @Test public void testSay(){ chinese.sayHello(); }
輸出結果你好
,此時按名字找到了chinese。
咱們上面也說了,咱們推薦是用@Inject,不會與Spring產生耦合,固然若是有必要也可使用@Autowired,爲何不推薦使用@Resouce呢?
如今咱們讓Chinese不實現Person接口,但仍然被Spring管理,測試代碼以下:
@Resource Person chinese; @Test public void testSay(){ chinese.sayHello(); }
結果以下:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'site.abely.service.TestTest': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'chinese' is expected to be of type 'site.abely.service.Person' but was actually of type 'site.abely.service.Chinese'
結果的問題在於,American是Person的惟一實現類並且被Spring託管,此時卻不會被注入。
使用@Autowired輸出Hello
對比你能夠發現@Resource的問題所在,也許你認爲@Resouce的結果是合理的,可是你要考慮到@Qualifier或@Named的做用,咱們能夠用以下代碼取得和@Resouce相似的效果:
@Autowired @Qualifier("chinese") Person chinese; @Test public void testSay() { chinese.sayHello(); }
出現異常:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'site.abely.service.Person' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=chinese)}
這樣也會去找一個bean id爲chinese的bean,在這種狀況下(只有一個實現類),@Resouce要想得到正確的注入只有一個方式,正確的命名Person american
或Person person
(person能成功的緣由在於Spring中沒有bean的id爲person的託管類),若是命名不正確,即便使用@Qualifier("american")
也不會正確注入(@Resouce不會鳥它的),這會給開發者帶來額外的負擔,即便只有一個實現類,@Resouce也有可能沒法注入。這也是開發中常見的事情,若是你的命名和Sping中某個bean的id相同,@Resouce會出現一些意想不到的問題。