1.3. Bean概述

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

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

  • 一个包限定的类名:通常是被定义的 bean 的实际实现类。

  • Bean 行为配置元素,它说明 bean 在容器中的行为方式(范围、生命周期回调等)。

  • 对 bean 完成工作所需的其他 bean 的引用。这些引用也称为协作者或依赖项。

  • 要在新创建的对象中设置的其他配置设置——例如,池的大小限制或在管理连接池的 bean 中使用的连接数。

此元数据转换为组成每个 bean 定义的一组属性。下表描述了这些属性:

Property解释...

Class

Name

Scope

Constructor arguments

Properties

Autowiring mode

Lazy initialization mode

Initialization method

Destruction method

除了包含有关如何创建特定 bean 的信息的 bean 定义之外,ApplicationContext的实现还允许注册在容器外部(由用户)创建的现有对象。这是通过 getBeanFactory() 方法访问 ApplicationContext 的 BeanFactory 来完成的,该方法返回 DefaultListableBeanFactory 实现。DefaultListableBeanFactory 通过 registerSingleton(..) registerBeanDefinition(..) 方法支持这种注册。然而,典型的应用程序仅使用通过常规 bean 定义元数据定义的 bean。

需要尽早注册 Bean 元数据和手动提供的单例实例,以便容器在自动装配和其他自省步骤中正确推理它们。虽然在某种程度上支持覆盖现有元数据和现有单例实例,但官方不支持在运行时注册新 bean(同时对工厂进行实时访问),并可能导致并发访问异常、bean 容器中的状态不一致或两个都。

1.3.1. Bean命名

每个 bean 都有一个或多个标识符。这些标识符在承载 bean 的容器中必须是唯一的。一个 bean 通常只有一个标识符。但是,如果它需要多个,则可以将多余的视为别名。

在基于 XML 的配置元数据中,您可以使用id属性、name属性或两者来指定 bean 标识符。该id属性可让您准确指定一个 id。按照惯例,这些名称是字母数字的(“myBean”、“someService”等),但它们也可以包含特殊字符。如果要为 bean 引入其他别名,也可以在name 属性中指定,用逗号 ( ,)、分号 ( ;) 或空格分隔。作为历史记录,在 Spring 3.1 之前的版本中,id属性被定义为一种xsd:ID类型,它限制了可能的字符。从 3.1 开始,它被定义为一种xsd:string类型。请注意,id容器仍然强制执行 bean 唯一性,但不再由 XML 解析器强制执行。

您不需要为 bean 提供nameid。如果您不显式提供 nameid,则容器会为该 bean 生成一个唯一名称。但是,如果您想通过名称引用该 bean,通过使用ref元素或服务定位器样式查找,您必须提供名称。不提供名称的动机与使用内部 bean自动装配合作者有关。

Bean 命名约定

约定是在命名 bean 时对实例字段名称使用标准 Java 约定。也就是说,bean 名称以小写字母开头,并且从那里开始是驼峰式的。此类名称的示例包括accountManageraccountServiceuserDaologinController等。

命名 bean 的一致性使您的配置更易于阅读和理解。此外,如果您使用 Spring AOP,则在将建议应用于一组按名称相关的 bean 时会很有帮助。

通过类路径中的组件扫描,Spring 为未命名的组件生成 bean 名称,遵循前面描述的规则:本质上,采用简单的类名称并将其初始字符转换为小写。但是,在(不寻常的)特殊情况下,当有多个字符并且第一个和第二个字符都是大写时,会保留原始大小写。这些与java.beans.Introspector.decapitalize(Spring 在此处使用的) 定义的规则相同。

在 Bean 定义之外为 Bean 起别名

在 bean 定义本身中,您可以为 bean 提供多个名称,方法是使用属性指定的最多一个名称id和属性name中任意数量的其他名称的组合。这些名称可以是同一个 bean 的等效别名,并且在某些情况下很有用,例如让应用程序中的每个组件通过使用特定于该组件本身的 bean 名称来引用公共依赖项。

但是,指定实际定义 bean 的所有别名并不总是足够的。有时需要为在别处定义的 bean 引入别名。这在大型系统中很常见,其中配置在每个子系统之间进行拆分,每个子系统都有自己的一组对象定义。在基于 XML 的配置元数据中,您可以使用<alias/>元素来完成此操作。以下示例显示了如何执行此操作:

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

在这种情况下,命名fromName的 bean(在同一容器中)也可以在使用此别名定义后称为toName.

例如,子系统 A 的配置元数据可能引用名为subsystemA-dataSource 的 DataSource 。子系统 B 的配置元数据可能会引用名称为 subsystemB-dataSource的 DataSource。在编写同时使用这两个子系统的主应用程序时,主应用程序通过名称 myApp-dataSource来引用 DataSource。要让所有三个名称都引用同一个对象,您可以将以下别名定义添加到配置元数据中:

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

现在每个组件和主应用程序都可以通过一个唯一的名称来引用数据源,并保证不会与任何其他定义冲突(有效地创建一个命名空间),但它们引用的是同一个 bean。

Java 配置

如果您使用 Java配置,则可以使用注解@Bean来提供别名。有关详细信息,请参阅使用@Bean注解

1.3.2. 实例化 Bean

bean 定义本质上是创建一个或多个对象的方法。当被访问时,容器会查看命名 bean 的定义,并使用该 bean 定义封装的配置元数据来创建(或获取)实际对象。

如果您使用基于 XML 的配置元数据,您可以在元素<bean/>class属性中指定要实例化的对象的类型(或类) 。 class属性(在内部是实例BeanDefinitionClass属性 )通常是必需的。(有关例外情况,请参阅 使用实例工厂方法Bean 定义继承进行实例化。)您可以通过以下两种方式之一使用Class属性:

  • 通常,在容器本身通过反射调用其构造函数直接创建 bean 的情况下,指定要构造的 bean 类,有点等价于 Java 代码中的new操作符。

  • 指定包含被调用以创建对象的static工厂方法的实际类,在不太常见的情况下,容器调用类上的static工厂方法来创建 bean。调用static工厂方法返回的对象类型可能是同一个类,也可能完全是另一个类。

嵌套类名

如果要为嵌套类配置 bean 定义,可以使用嵌套类的二进制名称或源名称。

例如,如果您在com.example包中调用了一个SomeThing类,并且SomeThing类有一个static名为OtherThing 的嵌套类,则它们可以用美元符号 ( $) 或点 ( .) 分隔。所以bean 定义中属性class的值是com.example.SomeThing$OtherThingcom.example.SomeThing.OtherThing

使用构造函数进行实例化

当您通过构造方法创建 bean 时,所有普通类都可以被 Spring 使用并兼容。也就是说,正在开发的类不需要实现任何特定的接口或以特定的方式进行编码。只需指定 bean 类就足够了。但是,根据您用于该特定 bean 的 IoC 类型,您可能需要一个默认(空)构造函数。

Spring IoC 容器几乎可以管理您希望它管理的任何类。它不仅限于管理真正的 JavaBean。大多数 Spring 用户更喜欢只有一个默认(无参数)构造函数以及根据容器中的属性建模的适当的 setter 和 getter 的实际 JavaBeans。您还可以在容器中拥有更多奇特的非 bean 样式类。例如,如果您需要使用绝对不符合 JavaBean 规范的遗留连接池,那么 Spring 也可以管理它。

使用基于 XML 的配置元数据,您可以指定 bean 类,如下所示:

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

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

有关在构造对象后向构造函数提供参数(如果需要)和设置对象实例属性的机制的详细信息,请参阅 注入依赖项

使用静态工厂方法进行实例化

在定义使用静态工厂方法创建的 bean 时,使用class 属性指定包含static工厂方法的类,使用命名属性factory-method指定工厂方法本身的名称。您应该能够调用此方法(使用可选参数,如后所述)并返回一个活动对象,该对象随后被视为是通过构造函数创建的。这种 bean 定义的一种用途是在遗留代码中调用static工厂。

以下 bean 定义指定将通过调用工厂方法创建 bean。定义没有指定返回对象的类型(类),而是包含工厂方法的类。在此示例中, createInstance()方法必须是static方法。以下示例显示了如何指定工厂方法:

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

下面的例子展示了一个可以与前面的 bean 定义一起工作的类:

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

    public static ClientService createInstance() {
        return clientService;
    }
}

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

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

与通过静态工厂方法进行实例化类似,使用实例工厂方法进行实例化会从容器中调用现有 bean 的非静态方法来创建新 bean。要使用此机制,请将class属性留空,并在factory-bean属性中指定当前(或父级或祖先)容器中的 bean 的名称,该容器包含要调用以创建对象的实例方法。使用属性factory-method设置工厂方法本身的名称。以下示例显示了如何配置这样的 bean:

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

以下示例显示了相应的类:

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

一个工厂类也可以包含多个工厂方法,如下例所示:

<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"/>

以下示例显示了相应的类:

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;
    }
}

这种方法表明,工厂 bean 本身可以通过依赖注入 (DI) 进行管理和配置。请参阅依赖项和配置详细信息

在 Spring 文档中,“工厂 bean”是指在 Spring 容器中配置并通过 实例静态工厂方法创建对象的 bean。相比之下, FactoryBean(注意大写)指的是特定于 Spring 的 FactoryBean实现类。

确定 Bean 的运行时类型

确定特定 bean 的运行时类型并非易事。bean 元数据定义中的指定类只是一个初始类引用,可能与声明的工厂方法结合,或者是可能导致 bean 的不同运行时类型的FactoryBean类,或者在实例的情况下根本没有设置实例级别工厂方法(而是通过指定的factory-bean名称解析)。此外,AOP 代理可以用基于接口的代理包装一个 bean 实例,并限制目标 bean 的实际类型(仅其实现的接口)的暴露。

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

最后更新于