了解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));
}
在上面的例子中,因为Lion和Tiger都实现了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()。这样,我们就可以避免将应用程序逻辑与框架相关的细节混在一起。