Understanding getBean() in Spring – 了解Spring中的getBean()

最后修改: 2019年 7月 1日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’re going to go through different variants of the BeanFactory.getBean() method.

在本教程中,我们将了解BeanFactory.getBean()方法的不同变体。

Simply put, as the name of the method also suggests, this is responsible for retrieving a bean instance from the Spring container.

简单地说,正如该方法的名称所示,this负责从Spring容器中检索一个Bean实例

2. Spring Beans Setup

2.Spring的Bean设置

First, let’s define a few Spring beans for testing. There are several ways in which we can provide bean definitions for the Spring container, but in our example, we’ll use annotation-based Java config:

首先,让我们定义几个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 {}
}

We’ve created two beans. Lion has the default singleton scope. Tiger is explicitly set to prototype scope. Additionally, please note that we defined names for each bean that we’ll use in further requests.

我们已经创建了两个Bean。Lion有默认的单子作用域。Tiger被明确设置为prototype scope。此外,请注意,我们为每个Bean定义了名字,我们将在进一步的请求中使用这些名字。

3. The getBean() APIs

3.getBean() APIs

BeanFactory provides five different signatures of the getBean() method that we’re going to examine in the following subsections.

BeanFactory提供了getBean()方法的五个不同的签名,我们将在下面的小节中考察。

3.1. Retrieving Bean by Name

3.1.按名称检索Bean

Let’s see how we can retrieve a Lion bean instance using its name:

让我们看看如何使用名字来检索一个Lion bean实例。

Object lion = context.getBean("lion");

assertEquals(Lion.class, lion.getClass());

In this variant, we provide a name, and in return, we get an instance of Object class if a bean with the given name exists in the application context. Otherwise, both this and all other implementations throw NoSuchBeanDefinitionException if the bean lookup fails.

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

The main disadvantage is that after retrieving the bean, we have to cast it to the desired type. This may produce another exception if the returned bean has a different type than we expected.

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

Suppose we try to get a Tiger using the name “lion”.  When we cast the result to Tiger, it will throw a ClassCastException:

假设我们试图用“狮子 “这个名字来获得一个老虎当我们把结果投给Tiger时,它将抛出一个ClassCastException

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

3.2. Retrieving Bean by Name and Type

3.2.按名称和类型检索Bean

Here we need to specify both the name and type of the requested bean:

这里我们需要指定所请求的Bean的名称和类型。

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

Compared to the previous method, this one is safer because we get the information about type mismatch instantly:

与之前的方法相比,这个方法更安全,因为我们可以立即得到类型不匹配的信息。

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

3.3. Retrieving Bean by Type

3.3.按类型检索Bean

With the third variant of getBean(), it is enough to specify only the bean type:

对于getBean()的第三个变体,只需指定Bean类型即可。

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

In this case, we need to pay special attention to a potentially ambiguous outcome:

在这种情况下,我们需要特别注意一个潜在的模糊的结果

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

In the example above, because both Lion and Tiger implement the Animal interface, merely specifying type isn’t enough to unambiguously determine the result. Therefore, we get a NoUniqueBeanDefinitionException.

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

3.4. Retrieving Bean by Name with Constructor Parameters

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

In addition to the bean name, we can also pass constructor parameters:

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

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

This method is a bit different because it only applies to beans with prototype scope.

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

In the case of singletons, we’re going to get a BeanDefinitionStoreException.

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

Because a prototype bean will return a newly created instance every time it’s requested from the application container, we can provide constructor parameters on-the-fly when invoking getBean():

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

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

assertEquals("Siberian", tiger.getName());
assertEquals("Striped", secondTiger.getName());

As we can see, each Tiger gets a different name according to what we specified as a second parameter when requesting the bean.

正如我们所看到的,每个Tiger都会根据我们在请求Bean时指定的第二个参数得到一个不同的名字。

3.5. Retrieving Bean by Type With Constructor Parameters

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

This method is analogous to the last one, but we need to pass the type instead of the name as the first argument:

这个方法与上一个方法类似,但我们需要传递类型而不是名字作为第一个参数。

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

assertEquals("Shere Khan", tiger.getName());

Similar to retrieving a bean by name with constructor parameters, this method only applies to beans with prototype scope.

与用构造函数参数按名称检索Bean类似,该方法仅适用于具有原型范围的Bean

4. Usage Considerations

4.使用方面的考虑

Despite being defined in the BeanFactory interface, the getBean() method is most frequently accessed through the ApplicationContext. Typically, we don’t want to use the getBean() method directly in our program.

尽管定义在BeanFactory接口中,但getBean()方法最常通过ApplicationContext访问。通常,我们不想在程序中直接使用getBean()方法

Beans should be managed by the container. If we want to use one of them, we should rely on dependency injection rather than a direct call to ApplicationContext.getBean(). That way, we can avoid mixing application logic with framework-related details.

Bean应该由容器来管理。如果我们想使用其中一个,我们应该依靠依赖性注入,而不是直接调用ApplicationContext.getBean()这样一来,我们就可以避免将应用逻辑与框架相关的细节混为一谈。

5. Conclusion

5.总结

In this quick tutorial, we went through all implementations of the getBean() method from the BeanFactory interface and described the pros and cons of each.

在这个快速教程中,我们浏览了来自BeanFactory接口的getBean()方法的所有实现,并描述了每种方法的优点和缺点。

All the code examples shown here are available over on GitHub.

这里显示的所有代码示例都可以在GitHub上找到