1.3 Bean概述

1.3 Bean概述 #

Version 5.2.2.RELEASE


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

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

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

  • bean行为的配置元素,它规定bean在容器中的行为(作用域、生命周期回调等)。

  • bean执行其工作时所需引用的其他bean。这些引用也称为协作者或依赖项。

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

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

表1. bean定义

属性 解释…
实例化bean
名称 命名bean
作用域 bean的作用域
构造函数参数 依赖注入
属性 依赖注入
自动装配模式 自动装配协作者
延迟初始化模式 延迟初始化bean
初始化方法 初始化时回调
销毁方法 销毁时回调

除了通过控制bean定义的信息来创建特定的bean之外,ApplicationContext实现还允许注册(由用户)在容器外部创建的现有对象。这是通过getBeanFactory()方法访问ApplicationContext的BeanFactory来完成的,该方法返回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类型。注意,bean id唯一性仍然由容器强制执行,尽管不再由XML解析器强制执行了。

您不需要为bean提供nameid。如果不显式提供nameid,容器将为该bean生成唯一的名称。但是,如果希望通过使用ref元素或服务定位器方式按名称引用该bean,则必须提供名称。不提供名称的内在原因与使用内部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的名称引用数据源。子系统B的配置元数据可以通过subsystemB-dataSource的名称引用数据源。在编写使用这两个子系统的主应用程序时,主应用程序以myApp-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属性(在内部是BeanDefinition实例上的Class属性)通常是必需的(有关例外情况,请参阅通过使用实例工厂方法实例化Bean定义继承。)您可以通过以下两种方式之一使用Class属性:

  • 通常,指定要构造的bean类,然后容器本身通过反射调用其构造函数直接创建bean的场景,这在某种程度上相当于使用new操作符的Java代码。
  • 指定包含静态工厂方法(通过调用该方法以创建对象)的实际类,在不常见的情况下,容器调用类上的静态工厂方法以创建bean。调用静态工厂方法返回的对象类型可以是同一个类,也可以是另一个类。

内部类命名

如果要为静态嵌套类配置bean定义,则必须使用嵌套类的二进制名称。

例如,如果在com.example包中有一个名为SomeThing的类,而这个SomeThing类中有一个名为OtherThing的静态嵌套类,那么bean定义上的class属性的值将是com.example.SomeThing$OtherThing

请注意,名称中使用$字符将嵌套类名与外部类名分开。

使用构造函数进行实例化 #

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

Spring IoC容器实际上可以管理您希望它管理的任何类。它不仅限于管理真正的JavaBean。大多数Spring用户更喜欢真正的JavaBean,它只有一个默认(无参数)构造函数,并根据容器中的属性建模适当的setter和getter。您的容器中还可以有更多异样的非bean样式的类。例如,如果您需要使用一个完全不符合JavaBean规范的老式连接池,Spring也可以管理它。

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

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

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

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

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

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

下面的bean定义是通过调用指定工厂方法来创建bean的。该定义不是指定返回对象的类型(类),只是指定包含工厂方法的类。在本例中,createInstance()方法必须是静态方法。以下示例展示了如何指定工厂方法:

<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;
    }
}
class ClientService private constructor() {
    companion object {
        private val clientService = ClientService()
        fun createInstance() = clientService
    }
}

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

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

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

<!-- 工厂bean,包含一个名为createInstance()的方法(注,此处的createInstance()指“创建实例”的方法) -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- 注入此定位器bean所需的任何依赖项 -->
</bean>

<!-- 要通过工厂bean创建的bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

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

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}
class DefaultServiceLocator {
    companion object {
        private val clientService = ClientServiceImpl()
    }
    fun createClientServiceInstance(): ClientService {
        return clientService
    }
}

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

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- 注入此定位器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;
    }
}
class DefaultServiceLocator {
    companion object {
        private val clientService = ClientServiceImpl()
        private val accountService = AccountServiceImpl()
    }

    fun createClientServiceInstance(): ClientService {
        return clientService
    }

    fun createAccountServiceInstance(): AccountService {
        return accountService
    }
}

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

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