1. 引言
本文将带你全面了解 Spring 中 BeanFactory.getBean()
方法的多种使用方式。
顾名思义,getBean()
的作用就是从 Spring 容器中获取 Bean 实例。虽然我们在日常开发中很少直接使用它,但在某些特定场景下(比如手动获取 Bean 或编写框架代码)非常有用。
2. Spring Bean 示例准备
我们先定义几个用于测试的 Spring Bean。本文采用基于注解的 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 {}
}
说明:
Lion
是默认的单例(singleton)Bean。Tiger
是原型(prototype)作用域的 Bean,并且我们给它定义了两个别名:tiger
和kitty
。- 我们还定义了一个公共接口
Animal
,为后续测试做准备。
3. getBean() 的五种 API 形式
BeanFactory
接口共提供了五种形式的 getBean()
方法,我们一一来看。
3.1 按名称获取 Bean
Object lion = context.getBean("lion");
assertEquals(Lion.class, lion.getClass());
这种方式通过 Bean 名称获取一个 Object
类型的实例。如果找不到该 Bean,会抛出 NoSuchBeanDefinitionException
。
⚠️ 缺点:返回的是 Object
类型,需要手动强转,容易引发 ClassCastException
。
例如:
assertThrows(ClassCastException.class, () -> {
Tiger tiger = (Tiger) context.getBean("lion");
});
3.2 按名称和类型获取 Bean
Lion lion = context.getBean("lion", Lion.class);
这种方式更安全,Spring 会在获取时进行类型检查。如果类型不匹配,会直接抛出 BeanNotOfRequiredTypeException
。
示例:
assertThrows(BeanNotOfRequiredTypeException.class, () ->
context.getBean("lion", Tiger.class));
✅ 推荐在已知 Bean 名称和类型时使用。
3.3 按类型获取 Bean
Lion lion = context.getBean(Lion.class);
这种方式只需要传入类型即可。但如果容器中存在多个相同类型的 Bean,就会抛出 NoUniqueBeanDefinitionException
。
例如:
assertThrows(NoUniqueBeanDefinitionException.class, () ->
context.getBean(Animal.class));
因为 Lion
和 Tiger
都实现了 Animal
接口,所以无法唯一确定要返回哪一个。
⚠️ 使用时要注意类型唯一性。
3.4 按名称 + 构造参数获取 Bean
Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");
这个方法允许我们传入构造参数。但只适用于原型(prototype)作用域的 Bean。
单例 Bean 调用此方法会抛出 BeanDefinitionStoreException
。
示例:
Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");
Tiger secondTiger = (Tiger) context.getBean("tiger", "Striped");
assertEquals("Siberian", tiger.getName());
assertEquals("Striped", secondTiger.getName());
✅ 每次调用都会创建一个新实例,并传入指定的构造参数。
3.5 按类型 + 构造参数获取 Bean
Tiger tiger = context.getBean(Tiger.class, "Shere Khan");
assertEquals("Shere Khan", tiger.getName());
这个方法与上一种类似,只不过传入的是 Bean 类型而不是名称。
⚠️ 同样只适用于原型作用域的 Bean。
4. 使用建议
虽然 getBean()
定义在 BeanFactory
接口中,但在实际开发中,我们通常通过 ApplicationContext
来调用它。
不过,在业务代码中直接使用 getBean()
是不推荐的。Spring 的核心理念是依赖注入(DI),我们应该通过注解(如 @Autowired
)或构造函数注入来获取 Bean,而不是手动调用 getBean()
。
这样做的好处包括:
- ✅ 降低与 Spring API 的耦合
- ✅ 提高代码可测试性
- ✅ 遵循 Spring 的设计哲学
5. 小结
我们系统地了解了 getBean()
的五种使用方式,并分析了它们各自的适用场景和潜在问题。
方法 | 是否推荐 | 说明 |
---|---|---|
getBean(String name) |
❌ | 返回 Object,需要强转 |
getBean(String name, Class<T> requiredType) |
✅ | 安全性更高 |
getBean(Class<T> requiredType) |
⚠️ | 注意类型唯一性 |
getBean(String name, Object... args) |
⚠️ | 仅适用于 prototype Bean |
getBean(Class<T> requiredType, Object... args) |
⚠️ | 同上,传入类型而非名称 |
总结一句话:能用依赖注入就别用 getBean(),真要用时,注意类型安全和作用域限制。