為了探尋 ‘@Resource’, ‘@Autowired’, 和‘@Inject’如何解決依賴注入中的問題,我建立了一個“Party”介面,和它的兩個實現類“Person”,“Organization”。這樣我就可以在注入Bean的時候不必使用具體型別(指使用介面型別即可)。這樣做也方便我研究當一個介面有多個實現類與之匹配的時候Spring是如何解決依賴注入的不確定性的。
public interface Party {}
package com.sourceallies.person;
...
@Component
public class Person implements Party {}
package com.sourceallies.organization;
...
@Component
public class Organization implements Party {}
複製程式碼
在Spring的配置檔案中設定使用 ‘@Component’註解的兩個實現類所在的包需要進行注入檢查
<context:component-scan base-package="com.sourceallies.organization"/>
<context:component-scan base-package="com.sourceallies.person"/>
複製程式碼
測試1:不明確的bean注入
這個測試驗證注入Party的時候,當它有多個實現類的情況
@Resource
private Party party;
@Autowired
private Party party;
@Inject
private Party party;
複製程式碼
以上三種情況丟擲同樣的 ‘NoSuchBeanDefinitionException’異常,單看異常名稱意味著不存在對應的Bean,不過詳細資訊中顯示找到了兩個Bean。
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.sourceallies.Party] is defined: expected single matching bean but found 2: [organization, person]
測試2:欄位名稱注入
@Resource
private Party person;
@Autowired
private Party person;
@Inject
private Party person;
複製程式碼
其中@Resource注入可以設定可選項‘ name’屬性,以下語法和上面的@Resource註解方式是等效的,但是@Autowired和@Inject就沒有類似的等價方式了。
@Resource(name="person")
private Party party;
複製程式碼
以上三種方式最終都被注入‘Person’ Bean。
測試3:欄位型別注入
@Resource
private Person party;
@Autowired
private Person party;
@Inject
private Person party;
複製程式碼
以上情況都將注入 ‘Person’ Bean。
測試4:以實現類的預設名進行注入
@Resource
@Qualifier("person")
private Party party;
@Autowired
@Qualifier("person")
private Party party;
@Inject
@Qualifier("person")
private Party party;
複製程式碼
以上情況都將注入 ‘Person’ Bean。
測試5:指定實現類的類名
在實現類中使用‘Qualifier’註解指定注入時使用的名稱
package com.sourceallies.person;
...
@Component
@Qualifier("personBean")
public class Person implements Party {}
複製程式碼
注入的時候同樣使用‘Qualifier’註解指定注入哪一個名稱的實現類
@Resource
@Qualifier("personBean")
private Party party;
@Autowired
@Qualifier("personBean")
private Party party;
@Inject
@Qualifier("personBean")
private Party party;
複製程式碼
以上情況都將注入 ‘Person’ Bean。
測試6:集合注入
@Resource
private List<Party> parties;
@Autowired
private List<Party> parties
@Inject
private List<Party> parties;
複製程式碼
以上情況都將注入List中兩個Bean。此方式同樣可以用‘@Qualifier’限定注入Bean,每一個滿足指定‘qualifier’的bean才會被注入到List中。
測試7:不良配置
用毫無關聯的‘bad’作為‘@Qualifier’指定的匹配名
@Resource
@Qualifier("bad")
private Party person;
@Autowired
@Qualifier("bad")
private Party person;
@Inject
@Qualifier("bad")
private Party person;
複製程式碼
這種情況下使用‘@Resource’註解將會忽略‘@Qualifier’配置,故而‘Person' Bean將被注入。 而後兩者將會丟擲 ‘NoSuchBeanDefinitionException’ 的錯誤資訊,因為找不到與’@Qualifier‘配置相匹配的bean。
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.sourceallies.Party] 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), @org.springframework.beans.factory.annotation.Qualifier(value=bad)}
總結
‘@Autowired’ 和‘@Inject’的報錯資訊完全相同,他們都是通過‘AutowiredAnnotationBeanPostProcessor’ 類實現的依賴注入,二者具有可互換性。
‘@Resource’通過 ‘CommonAnnotationBeanPostProcessor’ 類實現依賴注入,即便如此他們在依賴注入時的表現還是極為相近的,以下是他們在實現依賴注入時執行順序的概括:
@Autowired and @Inject
- Matches by Type
- Restricts by Qualifiers
- Matches by Name
@Resource
- Matches by Name
- Matches by Type
- Restricts by Qualifiers (ignored if match is found by name)
‘@Resource’在依據name注入的時候速度效能表現的比 ‘@Autowired’ 和‘@Inject’優越,但這是微不足道的,不足以作為優先選擇 ‘@Resource’的原因。我傾向於使用 ‘@Resource’是因為它配置起來更簡潔。
@Resource(name="person")
@Autowired
@Qualifier("person")
@Inject
@Qualifier("person")
複製程式碼
你也許會說使用欄位 預設 名稱作為注入時候的bean name,其他兩種方式就會一樣簡潔:
@Resource
private Party person;
@Autowired
private Party person;
@Inject
private Party person;
複製程式碼
確實如此。但是當你需要重構程式碼的時候又如何呢?使用’@Resource‘方式只需簡單修改name屬性即可,而無需觸及注入Bean的名稱(注入Bean的時候同意使用介面名稱)。所以我建議使用註解方式實現注入的時候遵循以下語法風格:
1.在你的元件中明確限定bean名稱而不是使用預設值[@Component("beanName")]。
2.同時使用’@Resource‘和它的’name'屬性 [@Resource(name="beanName")]。
3.避免使用‘@Qualifier’註解,除非你要建立一系列類似beans的集合。例如,你也許需要建立一個set集合來存放一系列“規則”定義。這個時候可以選擇‘@Qualifier'註解方式。這種方式使得將大量遵循相同規則的類放入集合中變得容易。
4.使用如下配置限定需要盡心元件掃描的包: [context:component-scan base-package="com.sourceallies.person"]。這樣做可以減小spring掃描很多無效的包的情況。 遵循以上原則能增強你的,註解風格的,spring配置的可讀性和穩定性。