Contents

了解Spring GetBean

1.简介

在本教程中,我们将介绍*BeanFactory.getBean()*方法的不同变体。 简单地说,正如该方法的名称所暗示的,它负责从 Spring 容器中检索一个 bean 实例

2. Spring Beans 设置

首先,让我们定义一些用于测试的 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 {}
}

我们已经创建了两个 bean。Lion具有默认的单例范围。Tiger被明确设置为原型范围 。此外,请注意,我们为每个 bean 定义了名称,我们将在进一步的请求中使用这些名称。

3. getBean() API

BeanFactory 提供了五种不同的*getBean()*方法签名,我们将在下面的小节中进行研究。

3.1. 按名称检索 Bean

让我们看看如何使用名称检索Lion bean 实例:

Object lion = context.getBean("lion");
assertEquals(Lion.class, lion.getClass());

在这个变体中,我们提供了一个名称,作为回报,如果具有给定名称的 bean 存在于应用程序上下文中,我们将获得一个Object 类的实例。否则,如果 bean 查找失败,此实现和所有其他实现都会抛出NoSuchBeanDefinitionException

主要缺点是在检索 bean 后,我们必须将其转换为所需的类型 。如果返回的 bean 的类型与我们预期的不同,这可能会产生另一个异常

假设我们尝试使用名称*“Lion”来获得Tiger* 。当我们将结果转换为Tiger时,它会抛出ClassCastException

assertThrows(ClassCastException.class, () -> {
    Tiger tiger = (Tiger) context.getBean("lion");
});

3.2. 按名称和类型检索 Bean

在这里,我们需要指定请求的 bean 的名称和类型:

Lion lion = context.getBean("lion", Lion.class);

与前一种方法相比,这种方法更安全,因为我们可以立即获得有关类型不匹配的信息:

assertThrows(BeanNotOfRequiredTypeException.class, () -> 
    context.getBean("lion", Tiger.class));
}

3.3. 按类型检索 Bean

使用*getBean()*的第三个变体, 只指定 bean 类型就足够了:

Lion lion = context.getBean(Lion.class);

在这种情况下,我们需要特别注意潜在的模棱两可的结果

assertThrows(NoUniqueBeanDefinitionException.class, () -> 
    context.getBean(Animal.class));
}

在上面的例子中,因为LionTiger都实现了Animal接口,仅仅指定类型并不足以明确地确定结果。因此,我们得到一个NoUniqueBeanDefinitionException

3.4. 使用构造函数参数按名称检索 Bean

除了bean名称,我们还可以传递构造函数参数:

Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");

这个方法有点不同,因为它只适用于具有原型作用域的 bean

在单例的情况下,我们将得到一个BeanDefinitionStoreException

因为原型 bean 每次从应用程序容器请求时都会返回一个新创建的实例,所以我们可以在调用 *getBean()*时即时提供构造函数参数

Tiger tiger = (Tiger) context.getBean("tiger", "Siberian");
Tiger secondTiger = (Tiger) context.getBean("tiger", "Striped");
assertEquals("Siberian", tiger.getName());
assertEquals("Striped", secondTiger.getName());

正如我们所见,根据我们在请求 bean 时指定的第二个参数,每个Tiger都会获得不同的名称。

3.5. 使用构造函数参数按类型检索 Bean

这个方法类似于最后一个,但我们需要传递类型而不是名称作为第一个参数:

Tiger tiger = context.getBean(Tiger.class, "Shere Khan");
assertEquals("Shere Khan", tiger.getName());

与使用构造函数参数按名称检索 bean 类似,此方法仅适用于具有原型作用域的 bean

4. 使用注意事项

尽管在BeanFactory接口中定义,但getBean()方法最常通过ApplicationContext 访问。通常,*我们不想在程序中直接使用*getBean()方法。 bean 应该由容器管理。如果我们想使用其中之一,我们应该依赖依赖注入 而不是直接调用ApplicationContext.getBean()。这样,我们就可以避免将应用程序逻辑与框架相关的细节混在一起。