Spring IoC容器是应用的核心,负责创建和管理Bean。创建单个Bean就像普通Java对象实例化,但同一类需要多个Bean实例时就会踩坑。本文介绍三种通过注解实现同一类多Bean实例的方法,帮你解决这个常见问题。

2. 使用Java配置

这是最简单粗暴的方式:用Java配置类直接定义多个Bean。假设有个Person类:

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,用@Bean注解定义多个实例:

@Configuration
public class PersonConfig {
    @Bean
    public Person personOne() {
        return new Person("Harold", "Finch");
    }

    @Bean
    public Person personTwo() {
        return new Person("John", "Reese");
    }
}

关键点

  • @Bean会以方法名作为Bean ID注册到BeanFactory
  • 可直接通过自动装配注入,如@Autowired Person personOne

缺点也很明显

  • 需要手动用new创建实例
  • Bean数量增加时配置类会膨胀
  • 本质是Java风格而非Spring风格方案

3. 使用@Component注解

通过继承结合@Component创建多Bean。先创建Person的子类:

@Component
public class PersonOne extends Person {
    public PersonOne() {
        super("Harold", "Finch");
    }
}
@Component
public class PersonTwo extends Person {
    public PersonTwo() {
        super("John", "Reese");
    }
}

配置类启用组件扫描:

@Configuration
@ComponentScan("com.baeldung.multibeaninstantiation.solution2")
public class PersonConfig {
}

注意

  • @ComponentScan让Spring自动发现带@Component的类
  • 实际创建的是子类Bean而非基类多实例

局限性

  • 并非真正创建同一类的多个实例
  • 增加继承关系会提高代码复杂度
  • 仅适用于子类无额外属性的场景

4. 使用BeanFactoryPostProcessor

终极方案:通过自定义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!");
    }

    @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");
    }
}

Person实现FactoryBean接口,并用@Qualifier标记:

@Qualifier(value = "personOne, personTwo")
public class Person implements FactoryBean<Object> {
    private String firstName;
    private String secondName;

    public Person() {
        // 初始化代码(可选)
    }

    @Override
    public Class<Person> getObjectType() {
        return Person.class;
    }

    @Override
    public Object getObject() throws Exception {
        return new Person();
    }

    public boolean isSingleton() {
        return true;
    }

    // getter & setter 省略
}

核心机制

  • FactoryBean作为Bean工厂,负责生成其他Bean
  • 类级别@Qualifier声明多个Bean名称

4.2 自定义BeanFactory实现

创建BeanFactoryPostProcessor实现类:

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(",");
    }
}

执行流程

  1. Spring初始化时触发postProcessBeanFactory
  2. 扫描带@Qualifier的类
  3. 解析注解中的Bean名称列表
  4. 为每个名称注册单例Bean

最后配置类集成所有组件:

@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
  • 缺点:实现复杂,不符合常规配置习惯
  • 适用:对注解有强需求的特殊场景

5. 总结

三种方案对比:

  • Java配置:最简单,适合少量Bean
  • @Component继承:避免手动创建,但需继承
  • BeanFactoryPostProcessor:纯注解实现,但代码复杂

根据实际场景选择:简单需求用前两种,强注解需求用第三种。源码见GitHub


原始标题:Instantiating Multiple Beans of the Same Class with Spring Annotations