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,并且我们给它定义了两个别名:tigerkitty
  • 我们还定义了一个公共接口 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));

因为 LionTiger 都实现了 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(),真要用时,注意类型安全和作用域限制。


原始标题:Understanding getBean() in Spring | Baeldung