1. 简介
在 Spring Boot 应用程序中,有时需要为同一个类创建多个实例(Bean),并且每个实例可能有不同的配置或用途。虽然可以通过在 @Configuration 配置类中定义多个 @Bean 方法来实现这一点,但在需要创建大量实例的情况下,这种方法不仅冗余,而且难以维护。
在本篇文章中,我们将学习如何在Spring框架中使用注解来创建同一类的多个beans。
2. 实战案例
2.1 使用Java配置
这是使用注解创建多个同类 bean 的最简单易行的方法。在这种方法中,我们将使用基于 Java 的配置类来配置同一类中的多个 Bean,如下示例:
public class Person {
private String name ;
private Integer age ;
public Person(String name, Integer age) {
this.name = name ;
this.age = age ;
}
// getters, setters
@Override
public String toString() {
return "Person [name=" + name+ ", age=" + age+ "]" ;
}
}
接下来,我们将构建一个名为 PersonConfig 的配置类,并在其中定义 Person 类的多个 Bean:
@Configuration
public class PersonConfig {
@Bean
public Person person1() {
return new Person("Pack", 22) ;
}
@Bean
public Person person2() {
return new Person("xxgg", 24) ;
}
}
在这里,@Bean 注解实例化两个bean,并将它们注册到Spring容器中。接下来,我们可以初始化Spring容器,并从Spring容器中请求任何bean。这种策略还使得实现依赖注入变得简单。我们可以使用自动装配直接将一个bean(例如person1)注入到同类型的另一个bean(例如person2)中。
这种方法的局限是,在典型的基于Java的配置风格中,我们需要使用new关键字手动实例化bean。因此,如果相同类的bean数量增加,我们需要先注册它们,并在配置类中创建这些bean。这使得它成为一种更偏向于Java的方法,而不是Spring特有的方法。
2.2 使用@Component注解
在这种方法中,我们将使用@Component注解来创建多个bean,这些bean将从Person类继承属性。首先,我们将创建多个子类,即PersonOne和PersonTwo,它们扩展自Person超类:
@Component
public class PersonOne extends Person {
public PersonOne() {
super("Pack", 22) ;
}
}
@Component
public class PersonTwo extends Person {
public PersonTwo() {
super("xxxooo", 24) ;
}
}
这种方法的问题是,它不会为同一个类创建多个实例。相反,它创建的是从超类继承属性的类的bean。因此,我们只能在继承类没有定义任何额外属性的情况下使用这种解决方案。此外,使用继承会增加代码的整体复杂性。
2.3 使用BeanFactoryPostProcessor
利用 BeanFactoryPostProcessor 接口的自定义实现来创建同一类的多个 Bean 实例。我们将通过以下步骤来实现:
- 创建自定义 Bean 类并使用 FactoryBean 接口对其进行配置
- 使用 BeanFactoryPostProcessor 接口实例化同一类型的多个 Bean
自定义 Bean 实现
为了更好地理解这种方法,我们将进一步扩展同一实力。假设有一个 Human 类,它依赖于 Person 类的多个实例:
public class Human implements InitializingBean {
private Person personOne;
private Person personTwo;
@Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(personOne, "Pack is alive!");
Assert.notNull(personTwo, "Jook is alive!");
}
@Autowired
public void setPersonOne(Person personOne) {
this.personOne = personOne ;
this.personOne.setName("Pack") ;
this.personOne.setAge(22) ;
}
@Autowired
public void setPersonTwo(Person personTwo) {
this.personTwo = personTwo ;
this.personTwo.setName("Jook") ;
this.personTwo.setAge(24) ;
}
}
InitializingBean 接口会调用 afterPropertiesSet() 方法来检查 BeanFactory 是否设置了所有 Bean 属性,以及是否满足了其他依赖条件。此外,我们将使用setter注入法注入并初始化两个 Person 类 Bean:personOne 和 personTwo。接下来,我们将创建一个实现 FactoryBean 接口的 Person 类。
@Qualifier(value = "personOne, personTwo")
public class Person implements FactoryBean<Object> {
private String name ;
private Integer age ;
public Person() {
}
public Class<Person> getObjectType() {
return Person.class ;
}
public Object getObject() throws Exception {
return new Person() ;
}
public boolean isSingleton() {
return true ;
}
}
这里要注意的是 @Qualifier 注解的使用,它包含了类级别上多个 Person 类型的名称或 bean id。在这种情况下,在类级别使用 @Qualifier 是有原因的,我们接下来就会看到。
自定义 BeanFactory 实现
任何实现 BeanFactoryPostProcessor 的类都会在创建任何 Spring 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 容器初始化后被调用。
最后,配置上面定义的Human及BeanFactoryPostProcessor
@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。