FactoryBean結合@Autowired和@Resource的使用產生的問題深究(二)
前一篇文章已經簡單介紹了一下FactoryBean的簡單使用,接下來講一下使用註解的時候遇到的問題。
先講一下背景(其實就是上一篇文章的例子):ToolFactory實現了FactoryBean的介面,併產生了Tool這個Bean,在xml檔案中也定義了ToolFactory這個bean:
<bean id="tool" class="pojo.ToolFactory"> <property name="factoryId" value="9090"/> <property name="toolId" value="1"/> </bean>
專案中我習慣是使用@Autowired註解的:
@Autowired
private ToolFactory tool;
這樣子使用,spring確實能夠找到這個bean,並能夠正確使用
@Resource和@Autowired
但是突然有一天,換成了@Resource,像這樣:
@Component
public class BlackBox {
@Resource
private ToolFactory tool;
@Override
public String toString() {
return " toolFactory=" + tool +
'}';
}
}
然後我獲取BlackBox裡面的東西測試的時候:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"classpath:applicationContext.xml");
BlackBox box = (BlackBox) context.getBean("blackBox");
System. out.println(box.toString());
}
產生了這個異常:
emm…這個是肯定的啊,@Resource預設按照名稱裝配bean,然後根據前一篇文章我們知道FactoryBean的getBean(“tool”)返回的型別是Tool,這裡指定為ToolFactory,肯定會有型別不匹配,但是為什麼使用@Autowired並指定ToolFactory型別卻可以正常呢?那是因為FactoryBean在產生Tool這個bean的時候,需要ToolFactory這個bean作為中間產物,也就是說spring其實是已經注入了ToolFactory的bean的,所以如果你用@Autowired並指定ToolFactory的時候,spring能夠找到ToolFactory型別的bean啊,所以肯定能夠正常執行
@Autowired結合@Qualifier
但是,我突然想起了@Qualifier這個註解,我在想@Qualifier和@Autowired一起使用時按照名稱裝配bean的(錯誤的根源就在這裡),那麼如果我指定@Qualifier的value屬性是“tool”,那麼它應該是Tool型別了吧,也就是說指定為ToolFactory型別會報錯的吧? 看下面:
@Component
public class BlackBox {
@Autowired
@Qualifier(value = "tool")
private ToolFactory tool;
@Override
public String toString() {
return " toolFactory=" + tool +
'}';
}
}
用前面的程式碼測試輸出BlackBox的toString():
額。。。完全正常!!!
這個時候我就慌了,於是我又把上面的型別指定為Tool再測試一下,發現還是正常(可以自行測試)
後面我就跑到stackoverflow去問:https://stackoverflow.com/questions/53361564/why-autowired-can-get-factorybean-type-and-bean-type
呼…總算搞明白了,一直是我理解錯了,其實@Autowired結合@Qualifier使用並不是單純的像@Resource一樣按照名字裝配bean,它主要適用的場景是當同一型別的bean被例項化了多個,然後名字不一樣的時候(準確的說應該是qualifier不一樣),就需要@Qualifier的value指定使用哪一個例項,也就是說,spring會先通過@Autowired判斷型別,然後通過@Qualifier的value確定具體是哪一個例項。所以上面的例子其實和 @Qualifier沒有關係,因為spring首先通過@Autowired確定了型別,Tool和FactoryTool的bean都存在,並且他們的qualifier都是tool(這裡要注意一下哦),所以就都可以裝配成功。
qualifier
但是我又注意到了一個詞—qualifier,這是個什麼東西?和xml中定義bean的時候的id有什麼區別?這裡參見這個博主的文章Spring 註解實現Bean依賴注入之@Qualifier,我總結一下:每個bean在依賴注入的時候都會有一個qualifier,預設情況下是bean的id,我們也可以根據<qualifier>
標籤顯式指定,像這樣:
<bean id="mysqlDataSourceBean" class="com.bean.MysqlDriveManagerDataSource">
<qualifier value="mysqlDataSource"/>
</bean>
總結
(1)FactoryBean在xml檔案中定義的bean型別是產物bean型別,但是中間也會生成FactoryBean型別的bean,通過@Autowired可以裝配產物bean和FactoryBean的例項,這兩種型別的bean的qualifier都是xml中定義的id名
(2)@Resource預設是按照名稱裝配,如果沒有顯式提供名字的時候,並且根據預設名字找不到對應的Spring管理物件,注入機制會回滾至型別匹配(可以自行測試),但是名字匹配的時候,發現型別不一致會丟擲異常
(3)和@Autowired結合@Qualifier的使用和@Resource的按照名字裝配是有區別的,spring首先根據@Autowired找到指定型別的bean,然後根據@Qualifier找到對應qualifier的bean
另外附上@Autowired和@Resource的區別比較好的一個解釋:
https://www.linkedin.com/pulse/difference-between-inject-vs-autowire-resource-pankaj-kumar
關鍵是最後一句話(這裡的name應該就是我們上面說的bean的id):
@Resource will narrow down the search first by name then by type and finally by Qualifiers (ignored if match is found by name). @Autowired and @Inject will narrow down the search first by type then by qualifier and finally by the name.