# Bean 概述

Spring IoC 容器管理一个或多个 bean。这些 bean 是使用您提供给容器的配置元数据创建的(例如,以 XML <bean/> 定义的形式)。

在容器本身中,这些 bean 定义表示为 BeanDefinition 对象,其中包含(以及其他信息)以下元数据:

  • 一个包限定的类名:通常是被定义的 bean 的实际实现类。
  • Bean 行为配置元素,用于说明 bean 在容器中的行为方式(范围、生命周期回调等)。
  • 对 bean 完成工作所需的其他 bean 的引用。这些引用也称为协作者或依赖项。
  • 要在新创建的对象中设置的其他配置设置——例如,池的大小限制或在管理连接池的 bean 中使用的连接数。

这些元数据转换为每个 beanDefinition 的一组属性,

Property 解释
Class (类) 实例化 Bean
Name (Bean 名称) Bean 的命名
Scope (Bean 的范围) Bean 的范围
Constructor arguments (构造参数) 依赖注入
Properties (属性) 依赖注入
Autowiring mode(自动装配模式) 自动装配协作者
Lazy initialization mode (懒加载模式) 延迟初始化的 Bean
Initialization method (初始化方法) 初始化回调
Destruction method (销毁方法) 销毁回调

除了通过 BeanDefinition 定义元数据让 Spring 创建 Bean 之外,ApplicationContext 还允许注册由用户自己创建的 Bean 到容器中

# 命名 Bean

每个 Bean 都有一个或多个唯一标识符(容器中唯一),一个 Bean 通常只有一个标识符,如果有多个,可以将多余的视为别名

在 XML 文件中可以使用 id 或 name 属性(或则两者来指定),一般使用字母数字来表示一个 Bean 的名称,如 myBean、someService 等。如果要指定多个名称的话可以使用英文 ,; 分隔。

如果想通过名称应用这个 Bean ,可以通过 ref 元素或则服务定位器查找;也可以不提供 id 或 name,容器会为该 Bean 生成一个唯一名称(一般采用简单的类名,并将首字母小写),不提供名称的动机一般是在 内部 Bean自动装配协作者 的场景中

# 在 Bean 定义之外为 Bean 起别名

前面说到,在 Bean 定义中可以为 Bean 提供多个名称,但是还可以在 Bean 定义外为 Bean 提供任意个等效的别名,比如下面这样

<alias name="fromName" alias="toName"/>
1

这个使用场景一般是在大型系统中,或则在一些应用程序中的每个组件通过使用特定于该组件本身的 bean 名称来引用公共的依赖项。

比如下面这个,A 组件使用别名 subsystemA-dataSource 来引用一个公共的数据源,每个组件和主应用程序都可以通过一个唯一的名称来引用数据源,并且保证不会与任何其他定义冲突(有效地创建命名空间),但它们引用的是同一个 bean。

<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
1
2

:::tips JAVA 配置中的 @Bean 中也可以配置别名 :::

# 实例化 Bean

bean 定义本质上是创建一个或多个对象的方法,当需要创建实例时,容器会根据 bean 定义中的配置元数据来创建(或获取)实际的对象

也就是 bean 标签中指定的 class 属性。指定的是一个类的限定名称,如果是嵌套类名,如下面这种

package com.example;
class SomeThing {
	static class OtherThing {}
} 
1
2
3
4

那么 OtherThing 的类名可以使用美元符号 ($) 或点 (.) 分隔: com.example.SomeThing$OtherThingorcom.example.SomeThing.OtherThing

# 使用构造函数实例化

使用构造函数进行实例化时,所有普通类都可以被 Spring 所使用并兼容,一般会使用默认的空参构造函数实例化。使用默认的无惨构成函数时,下面这样定义就可以了

<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
1
2
3

当然也可以使用有参构造函数实例化,这个需要参阅后续的 注入依赖项 (opens new window)

# 使用静态工厂方法

如下面这个类

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}
1
2
3
4
5
6
7
8

想要使用 createInstance 这个静态方法实例化 ClientService,可以使用 bean 标签中的 factory-method 属性

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
1
2
3

有关在工厂返回对象后向工厂方法提供(可选)参数和设置对象实例属性的机制的详细信息,请参阅 依赖项和配置详述 (opens new window)

# 使用实例工厂方法进行实例化

与静态工厂方法类似,不过使用实例工厂方法进行实例化是从容器中调用现有 bean 的非静态方法来创建新 bean。

比如要调用 createClientServiceInstance 获取实例

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}
1
2
3
4
5
6
7
8

就可以这样写

<!-- 空参构造实例化 DefaultServiceLocator -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- 注意这里没有 class 属性,使用 factory-bean 指向容器中已有的 bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
1
2
3
4
5
6
7
8
9

这样一来,你就可以在一个实例对象中定义多个工厂方法了,如下所示

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>
1
2
3
4
5
6
7
8
9
10
11

这种方法表明, factory bean 本身可以通过依赖注入 (DI) 进行管理和配置 :::tips 在 Spring 文档中,「factory bean」是指在 Spring 容器中配置并通过 实例 或 静态工厂方法 创建对象的 bean。相比之下, FactoryBean(注意大写)指的是特定于 Spring 的 FactoryBean 实现类。 :::

# 确定 Bean 的运行时类型

特定 bean 的运行时类型很难确定。bean 元数据定义中的指定类只是一个初始类引用,可能与声明的工厂方法结合,或者是一个 FactoryBean 类,可能导致 bean 的不同运行时类型的类,或则像上面那样使用实例工厂方法进行实例化的场景,仅通过 factory-bean 指定的名称解析。此外,AOP 代理可以使用基于接口的代理来包装 bean 实例,并有限地公开目标 bean 的实际类型(仅其实现的接口)。

了解特定 bean 的实际运行时类型的推荐方法是 BeanFactory.getType 调用指定的 bean 名称。这将考虑上述所有情况,并返回 BeanFactory 所需的对象类型。getBean调用将返回相同的bean名称。

所以可以使用:

  • BeanFactory.getType("beanName") :获取对应 bean 的 Class 对象
  • BeanFactory.getBean("beanName"):则是获取对应 bean 的 实例对象