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(",");
}
}
执行流程:
- Spring初始化时触发
postProcessBeanFactory
- 扫描带
@Qualifier
的类 - 解析注解中的Bean名称列表
- 为每个名称注册单例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。