1. 概述
Spring IoC 容器創建和管理 Spring Bean,這些 Bean 是我們應用程序的核心。 創建 Bean 的實例與從純 Java 類創建對象相同。 但是,生成同一類的多個 Bean 可能會帶來挑戰。
在本教程中,我們將學習如何使用 Spring 框架中的註解來創建同一類的多個 Bean。
2. 使用 Java 配置
這是創建相同類多個 Bean 的最簡單、最便捷的方法,通過註解實現。
在這種方法中,我們將使用基於 Java 的配置類來配置相同類中的多個 Bean。
讓我們考慮一個簡單的例子。我們有一個 Person 類,該類具有兩個類成員,firstName 和 lastName:
public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String secondName) {
super();
this.firstName = firstName;
this.lastName = secondName;
}
@Override
public String toString() {
return "Person [firstName=" + firstName + ", secondName=" + lastName + "]";
}
}接下來,我們將構建一個名為 PersonConfig 的配置類,並在其中定義多個 Person 類的 Bean:
@Configuration
public class PersonConfig {
@Bean
public Person personOne() {
return new Person("Harold", "Finch");
}
@Bean
public Person personTwo() {
return new Person("John", "Reese");
}
}這裏, 實例化了兩個具有與方法名稱相同的 id 的 bean,並在 BeanFactory (Spring 容器) 接口中註冊它們。 接下來,我們可以初始化 Spring 容器,並從 Spring 容器中請求任何 bean。 這種策略也使得實現依賴注入變得簡單。 我們可以直接將一個 bean,例如 personOne,注入到同類型的另一個 bean 中,例如 personTwo,使用自動裝配。
這種方法的侷限性在於,我們需要使用 new 關鍵字在典型的基於 Java 的配置風格中手動實例化 bean。 因此,如果相同類的 bean 數量增加,我們需要首先註冊它們,然後在配置類中創建 bean。 這使得它更具 Java 特性,而不是更具 Spring 特性。
3. 使用 @Component 註解
在這種方法中,我們將使用 @Component 註解來創建多個 Bean,這些 Bean 將繼承其屬性自 Person 類。首先,我們將創建多個子類,即 PersonOne 和 PersonTwo,這些子類將擴展 Person 超類:
@Component
public class PersonOne extends Person {
public PersonOne() {
super("Harold", "Finch");
}
}@Component
public class PersonTwo extends Person {
public PersonTwo() {
super("John", "Reese");
}
}接下來,在 PersonConfig 文件中,我們將使用 @ComponentScan 註解,以啓用整個包中的組件掃描。 這允許 Spring 容器自動創建任何帶有 @Component 註解的類的 Bean:
@Configuration
@ComponentScan("com.baeldung.multibeaninstantiation.solution2")
public class PersonConfig {
}現在我們可以直接使用 Spring 容器中的 PersonOne 或 PersonTwo Bean。 在其他地方,我們可以使用 Person 類 Bean。 這種方法的問題在於它不會創建相同類的多個實例。 相反,它創建了從超類繼承屬性的 Bean。
因此,我們只能在繼承類未定義任何附加屬性的情況下使用此解決方案。 此外,繼承的使用會增加代碼的整體複雜性。
4. 使用 <em>BeanFactoryPostProcessor</em> 接口
第三種和最終的方法利用自定義的 `BeanFactoryPostProcessor` 接口,用於創建同一類的多個 Bean 實例
可以通過以下步驟實現:
- 創建自定義 Bean 類並使用 `FactoryBean` 接口進行配置
- 使用 `BeanFactoryPostProcessor` 接口實例化同一類型的多個 Bean
4.1. 自定義 Bean 實現
為了更好地理解這種方法,我們將繼續擴展相同的示例。假設存在一個 Human 類,該類依賴於多個 Person 類的實例:
public class Human implements InitializingBean {
private Person personOne;
private Person personTwo;
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(personOne, "Harold is alive!");
Assert.notNull(personTwo, "John is alive!");
}
/* Setter injection */
@Autowired
public void setPersonOne(Person personOne) {
this.personOne = personOne;
this.personOne.setFirstName("Harold");
this.personOne.setSecondName("Finch");
}
@Autowired
public void setPersonTwo(Person personTwo) {
this.personTwo = personTwo;
this.personTwo.setFirstName("John");
this.personTwo.setSecondName("Reese");
}
}Spring 框架中的 InitializingBean 接口會調用 afterPropertiesSet() 方法來檢查 BeanFactory 是否已設置所有 Bean 屬性並滿足其他依賴關係。 此外,我們使用 setter 注入注入並初始化兩個 Person 類 Bean,personOne 和 personTwo。 接下來,我們將實現 FactoryBean 接口的 Person 類。
FactoryBean 充當 IoC 容器中創建其他 Bean 的工廠。此接口旨在創建實現該接口的 Bean 的更多實例。在本例中,它生成 Person 類類型的實例,並自動進行配置:
@Qualifier(value = "personOne, personTwo")
public class Person implements FactoryBean<Object> {
private String firstName;
private String secondName;
public Person() {
// initialization code (optional)
}
@Override
public Class<Person> getObjectType() {
return Person.class;
}
@Override
public Object getObject() throws Exception {
return new Person();
}
public boolean isSingleton() {
return true;
}
// code for getters & setters
}需要注意的是,另一個重要的方面是使用了 @Qualifier 註解,該註解在類級別上包含多個 Person 類型的名稱或 Bean ID。使用 @Qualifier 在類級別上的原因,將在下一部分進行説明。
4.2. 自定義 BeanFactory 實現
現在我們將使用 BeanFactoryPostProcessor 接口的自定義實現。
任何實現 BeanFactoryPostProcessor 接口的類在任何 Spring Bean 創建之前都會被執行。這允許我們配置和操作 Bean 生命週期。
BeanFactoryPostProcessor 會掃描所有帶有 @Qualifier 註解的類。 此外,它從該註解中提取名稱(Bean ID),並手動創建具有指定名稱的該類實例:
public class PersonFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Map<String, Object> map = beanFactory.getBeansWithAnnotation(Qualifier.class);
for (Map.Entry<String, Object> entry : map.entrySet()) {
createInstances(beanFactory, entry.getKey(), entry.getValue());
}
}
private void createInstances(ConfigurableListableBeanFactory beanFactory, String beanName, Object bean) {
Qualifier qualifier = bean.getClass().getAnnotation(Qualifier.class);
for (String name : extractNames(qualifier)) {
Object newBean = beanFactory.getBean(beanName);
beanFactory.registerSingleton(name.trim(), newBean);
}
}
private String[] extractNames(Qualifier qualifier) {
return qualifier.value().split(",");
}
}這裏,自定義的 BeanFactoryPostProcessor 實現會在 Spring 容器初始化後被調用一次。
接下來,為了保持簡潔,我們將使用 Java 配置類來初始化自定義的 BeanFactory 實現。
@Configuration
public class PersonConfig {
@Bean
public PersonFactoryPostProcessor PersonFactoryPostProcessor() {
return new PersonFactoryPostProcessor();
}
@Bean
public Person person() {
return new Person();
}
@Bean
public Human human() {
return new Human();
}
}這種方法的侷限性在於其複雜性。此外,不建議使用它,因為它不是在典型 Spring 應用中配置 Bean 的自然方式。 儘管存在這些侷限性,但這種方法更具 Spring 特性,用於通過註解實例化多個相同類型的 Bean。
5. 結論
在本文中,我們學習瞭如何使用 Spring 註解實例化同一類中多個 Bean 的三種不同方法。前兩種方法是簡單、特定於 Java 的實例化多個 Spring Bean 的方式。第三種方法則稍微複雜一些,但它通過註解實現 Bean 的創建。