前一篇文章已經簡單介紹了一下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.