1. 簡介
本教程將介紹不同變體的 <em>BeanFactory.getBean()</em> 方法。
簡單來説,正如方法名稱所示,此方法負責從 Spring 容器中檢索 Bean 實例。
2. Spring Bean 配置
首先,我們為測試定義幾個 Spring Bean。提供 Bean 定義給 Spring 容器有多種方式,但在本例中,我們將使用基於註解的 Java 配置:
@Configuration
class AnnotationConfig {
@Bean(name = {"tiger", "kitty"})
@Scope(value = "prototype")
Tiger getTiger(String name) {
return new Tiger(name);
}
@Bean(name = "lion")
Lion getLion() {
return new Lion("Hardcoded lion name");
}
interface Animal {}
}
我們創建了兩個 Bean。 獅子 具有默認的 Singleton 作用域。 老虎 明確設置為 Prototype 作用域。 此外,我們為每個 Bean 定義了名稱,並在後續請求中使用這些名稱。
3. Bean 獲取 API
<em>BeanFactory</em> 提供了以下五種不同的 getBean() 方法簽名,將在後續子章節中進行詳細分析。
3.1. 通過名稱檢索 Bean
讓我們看看如何使用其名稱檢索 獅子 Bean 實例:
Object lion = context.getBean("lion");
assertEquals(Lion.class, lion.getClass());在這一變體中,我們提供一個名稱,並返回一個應用程序上下文中具有該名稱的 Object 類的實例。如果找不到具有給定名稱的 Bean,則此 Bean 和所有其他實現都會拋出 NoSuchBeanDefinitionException。
主要的缺點是在檢索 Bean 後,我們必須將其強制轉換為所需類型。如果返回的 Bean 的類型與我們期望的不同,則可能會拋出異常。
假設我們嘗試使用名稱 “lion”獲取 Tiger。將結果強制轉換為 Tiger 時,將會拋出 ClassCastException:
assertThrows(ClassCastException.class, () -> {
Tiger tiger = (Tiger) context.getBean("lion");
});3.2. 通過名稱和類型檢索 Bean
這裏我們需要指定請求的 Bean 的名稱和類型:
Lion lion = context.getBean("lion", Lion.class);相比於之前的方案,這個方案更安全,因為我們能夠立即獲得類型不匹配的信息:
assertThrows(BeanNotOfRequiredTypeException.class, () ->
context.getBean("lion", Tiger.class));
}3.3. 通過類型檢索 Bean
使用第三種 `<em getBean() 變體,只需指定 Bean 的類型即可:
Lion lion = context.getBean(Lion.class);在這種情況下,我們需要特別注意一個可能存在歧義的潛在結果:
assertThrows(NoUniqueBeanDefinitionException.class, () ->
context.getBean(Animal.class));
}在上面的示例中,由於 Lion 和 Tiger 都實現了 Animal 接口,僅僅指定類型並不能明確地確定結果。因此,我們得到了 NoUniqueBeanDefinitionException。
3.4. 通過構造函數參數檢索 Bean
除了 Bean 名稱之外,我們還可以傳遞構造函數參數:
Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");此方法略有不同,因為它僅適用於具有原型作用域的 Bean。
對於單例 Bean,我們將收到 BeanDefinitionStoreException。
因為原型 Bean 在從應用程序容器中請求時,每次都會返回一個新的實例,因此在調用 getBean() 時,我們可以提供運行時構造參數:
Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");
Tiger secondTiger = (Tiger) context.getBean("tiger", "Striped");
assertEquals("Siberian", tiger.getName());
assertEquals("Striped", secondTiger.getName());如我們所見,每個 Tiger 都根據我們請求 Bean 時指定的第二個參數獲得不同的名稱。
3.5. 通過構造參數檢索 Bean
此方法類似於前一個方法,但我們需要將類型而不是名稱作為第一個參數傳遞:
Tiger tiger = context.getBean(Tiger.class, "Shere Khan");
assertEquals("Shere Khan", tiger.getName());與通過構造參數按名稱檢索 Bean 類似,此方法僅適用於具有原型作用域的 Bean。
4. 使用注意事項
儘管 <em>BeanFactory </em> 接口中定義了該方法,但 <em>getBean() </em> 方法通常通過 <em>ApplicationContext </em> 訪問。通常情況下,我們不希望在我們的程序中直接使用 <em>getBean()</em> 方法。
豆子應該由容器管理。如果我們想使用它們,我們應該依賴於依賴注入,而不是直接調用 `ApplicationContext.getBean()。 這樣可以避免將應用程序邏輯與框架相關細節混合在一起。
5. 結論
在本快速教程中,我們詳細介紹了 getBean() 方法的所有實現,這些實現來自 BeanFactory 接口,並對每種實現進行了優缺點分析。