4.3 Bean 概述

Spring 的 IoC 容器管理一个或多个 bean。这些 bean 通过提供给容器的配置元数据被创 建出来,比如,在 XML 中的<bean/>定义的形式。

在容器本身,这些 bean 代表了 BeanDefinition 对象,它们包含(在其它信息中) 下列元数据:

打包的类限定名:就是这些 bean 的真正实现类。

bean 的行为配置元素,这表示了 bean 在容器中(范围,生命周期回调等等)应该是怎 样的行为。

对其它 bean 的引用,这是该 bean 工作所需要的;这些引用通常被称为合作者或依赖。

在新被创建的对象中的其它配置设置,比如,管理连接池的 bean 中使用的连接数或连 接池限制的大小。

元数据翻译成一组属性集合来构成每一个 bean 的定义。

表 4.1 bean 定义

属性 解释章节
class 4.3.2 节,“实例化 bean”
name 4.3.1 节,“命名 bean”
scope 4.5 节,“bean 的范围”
constructor arguments 4.4.1 节,“依赖注入”
properties 4.4.1 节,“依赖注入”
autowiring mode 4.4.5 节,“装配合作者”
lazy-initialization mode 4.4.4 节,“延迟初始化 bean”
initialization method 4.6.1.1 节,“初始化回调”
descruction method 4.6.1.2 节,“销毁回调”

此外,bean 的定义还包含如何创建特定 bean 的信息,ApplicationContext 的实现类允许用户将容器外部创建的已有对象的注册。这可以通过 getBeanFactory()方法访问 ApplicationContext 的 BeanFactory 来完成 , 它 会 返 回 BeanFactory 的实现类 DefaultListableBeanFactory 。 DefaultListableBeanFactory 通 过 registerSingleton(..)和 registerBeanDefinition(..)方法来支持这种注册。 而典型的应用程序只和通过元数据定义的 bean 来工作。

4.3.1 命名 bean

每个 bean 有一个或者多个标识符。这些标识符必须在托管 bean 的容器内唯一。通常 一个 bean 只有一个标识符,但如果它需要多个的话,其它的可以被认为是别名。

在基于 XML 的配置元数据中,你可以使用 id 和/或 name 属性来指定 bean 的标识符。 id 属性允许你指定一个准确的 id。按照管理这些名称是字母和数字(‘myBean’,‘fooService’ 等),但是可能是特殊字符。如果你想为 bean 引入别名,你也可以在 name 属性中来指定,

通过逗号(,),分号(;)或空格来分隔。作为对历史的说明,在 Spring 3.1 版之前,id 属性 是 xsd:ID 类型,只限定为字符。在 3.1 版本中,现在是 xsd:string 类型了。要注意 bean 的 id 的唯一性还是被容器所强制的,但是对于 XML 处理器却不是。

当然你也可以不给 bean 提供 name 或 id。如果没有提供明确的 name 或 id,那么容器会为这个 bean 生成一个唯一的名称。然而,如果你想通过名称来参考这个 bean,那么可以 使用 ref 元素或者是服务定位器(4.15.2 节)风格的查找,你必须提供一个名称。不提供名称的动机是和使用内部 bean(4.4.2.3 节)或自动装备合作者(4.4.5 节)相关的。

Bean 的命名约定

该约定是用于当命名 bean 时,对实例字段名称的标准 Java 约定。也就是说,bean 的名称以小写字母开始,后面是驼峰形式的。这样的命名可以是(没有引号)‘accountManager’,‘accountService’,‘userDao’,‘loginController’ 等。

一致的命名 bean 可以使你的配置信息易于阅读和理解,如果你使用 Spring 的 AOP, 当应用通知到一组相关名称的 bean 时,它会给你很大的帮助。

4.3.1.1 在 bean 定义外面起别名

在 bean 定义的本身,你可以为 bean 提供多于一个的名称,使用一个由 id 属性或 name 属性中的任意名称数指定的名称组合。这些名称对于同一个 bean 来说都是相等的别名,在 一些情况下,这是很有用的,比如在应用程序中允许每个组件参考一个共同的依赖,可以使 用为组件本身指定的 bean 的名称。

然而,在 bean 定义时指定所有的别名是不够的。有事可能想在任意位置,为一个 bean 引入一个别名。这在大型系统中是很常见的例子,其中的配置信息是分在每个子系统中的, 每个子系统都有它自己的对象定义集合。在基于 XML 的配置元数据中,你可以使用<alias/> 元素来完成这项工作。

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

在这个示例中,在相同容器中的名称为 fromName 的 bean,在使用过这个别名定义后, 也可以使用 toName 来指引。

比如,在子系统的配置元数据中,A 可能要通过名称‘subsystemA-dataSource’来指向 数据源。为子系统 B 的配置元数据可能要通过名称‘subsystemB-dataSource’来指向数据源。

当处理使用了这两个子系统的主程序时,主程序要通过名称‘myApp-dataSource’来指向数 据源。那么就需要三个名称指向同一个对象,那么就要按照下面的别名定义来进行添加

MyApp 的配置元数据:

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

现在每个组件和主程序都可以通过一个唯一的保证不冲突的名称来还有其它任意定义(更有效地是创建命名空间)来参照数据源了,当然它们参照的是同一个 bean。

4.3.2 实例化 bean

bean 的定义信息本质上是创建一个或多个对象的方子。当需要一个 bean 时,容器查找 这些方子来查找命名的 bean,并且使用由 bean 定义信息封装的配置元数据来创建(或获得) 一个真实的对象。

如果你使用基于 XML 的配置元数据,你为要被实例化的对象所指定的类型(或类)是 <bean/>元素中 class 属性。这个 class 属性,是 BeanDefinition 实例内部的 cla ss 属性,通常是强制要有的。(对于特例,可以参考 4.3.2.3 节,“使用实例的工厂方法来实例化”和 4.7 节,“Bean 定义的继承”)你可以以两种方法之一来使用 class 属性:

典型的是,指定要被构造的 bean 类,容器本身直接通过反射调用它的构造方法来创建bean,也就是和 Java 代码中使用 new 操作符是相同的。

指定包含 static 工厂方法的真实的类,会被调用来创建对象,在一些不太常见的情况下,容器会调用类的 static 工厂方法来创建 bean。从调用 static 工厂方法返回的对象类型可能 是和另外一个类完全相同的类。

内部类名称

如果你想为 static 嵌入的类配置 bean,你需要使用内部类的二进制名称。 比如,如果在 com.example 包下有一个 Foo 类,而这个 Foo 类有一个 static 的内部类 Bar,那么在定义 bean 时’class’属性的值就会是...

com.example.Foo$Bar

请注意名称中$符号的使用,用来从外部类名中分隔内部类名。

4.3.2.1 使用构造方法实例化

当你使用构造方法来创建 bean 时,所有普通的类的使用都和 Spring 兼容。也就是说, 开发中的 bean 不需要实现任何特定的接口或以特定的方式来编码。仅简单指定 bean 的类 就足够了。但基于你使用的是什么类型的 IoC,就可能需要一个默认(空的)构造方法。

Spring 的 IoC 容器可以虚拟地管理任意的你想让它管理的类;而不仅仅限于管理真正的 JavaBean。很多 Spring 用户喜欢在容器中使用有默认(无参数)构造方法和在其后有适当 setter 和 getter 方法的真正 JavaBean。你也可以在容器中使用很多异样的,非 bean 样式的类。比 如,需要使用遗留的连接池,但是它没有符合 JavaBean 的规范,Spring 也能照样管理它。

基于 XML 的配置元数据,你可以如下来定义 bean:

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

关于提供构造方法参数(如果需要)和在对象被构造后,设置对象实例属性机制的详情, 请参考 4.4.1 节依赖注入。

4.3.2.2 使用静态工厂方法来实例化

当使用静态工厂方法来定义 bean 的时候,可以使用 class 属性来指定包含 static 工厂 方法的类,而名为 factory-method 的属性来指定静态方法。你应该调用这个方法(还可 以有可选的参数)并返回一个实际的对象,随后将它视为是通过构造方法创建的一样。在遗留代码中,这样定义 bean 的使用方式之一是调用 static 工厂。

下面 bean 的定义指定了要通过调用工厂方法生成的 bean。这个定义没有指定返回对象 的类型(类),仅仅是包含工厂方法的类。在本例中,createInstance()方法必须是静 态方法。

<bean id="clientService" class="examples.ClientService" factory-method="createInstance"/>
public class ClientService {
    private static ClientService clientService = new
        ClientService();
    private ClientService() {}
    public static ClientService createInstance() {
        return clientService;
    }
}

关于提供(可选的)参数到工厂方法并在对象由工厂返回后设置对象实例属性的机制详 情,请参考 4.4.2 节深入依赖和配置。

4.3.2.3 使用实例工厂方法来实例化

和使用静态工厂方法(4.3.2.2 节)实例化相似,使用实例工厂方法实例化是要调用容器 中已有 bean 的一个非静态的方法来创建新的 bean。要使用这个机制,请把 class 属性留 空,但在 factory-bean 属性中指定当前(或父/祖先)容器中 bea n 的名字,该 bean 要 包含被调用来创建对象的实例方法。使用 factory-method 方法来设置工厂方法的名称。

<!-- 工厂bean,包含名为createInstance()的方法 -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- 为locator bean注入任意需要的依赖 -->
</bean>
<!-- 通过工厂bean来创建的bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {
    private static ClientService clientService = new
        ClientServiceImpl();
    private DefaultServiceLocator() {}
    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

一个工厂类也可以有多于一个工厂方法,比如下面这个:

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!--为locator bean注入任意需要的依赖 -->
</bean>
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
<bean id="accountService" factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>
public class DefaultServiceLocator {
    private static ClientService clientService = new
    ClientServiceImpl();
    private static AccountService accountService = new
    AccountServiceImpl();
    private DefaultServiceLocator() {}
    public ClientService createClientServiceInstance() {
        return clientService;
    }
    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

这个方法展示了工厂 bean 本身可以通过依赖注入(DI)被管理和配置。请参考 4.4.2 节

深入依赖和配置。 注意在 Spring 文档中,工厂 bean 指的是在 Spring 容器中配置的 bean,可以通过实例(4.3.2.3节)或静态(4.3.2.2 节)工厂方法来创建对象。与此相反的是,FactoryBean(注意大小写)指的是 Spring 特定的 FactoryBean(4.8.3 节)