对于最新的稳定版本,请使用 Spring Framework 7.0.6!spring-doc.cadn.net.cn

Bean 概述

Spring IoC 容器管理一个或多个 Bean。这些 Bean 是根据您提供给容器的配置元数据创建的(例如,以 XML 形式 <bean/> 定义)。spring-doc.cadn.net.cn

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

  • 一个包限定的类名:通常,是正在定义的 bean 的实际实现类。spring-doc.cadn.net.cn

  • Bean 行为配置元素,用于说明 Bean 在容器中的行为(作用域、生命周期回调等)。spring-doc.cadn.net.cn

  • 引用其他bean,这些bean是该bean完成其工作所需的。这些引用也称为协作对象或依赖项。spring-doc.cadn.net.cn

  • 在新创建的对象中设置的其他配置选项——例如,池的大小限制或管理连接池的 bean 使用的连接数量。spring-doc.cadn.net.cn

此元数据转换为构成每个 bean 定义的一组属性。 下表描述了这些属性:spring-doc.cadn.net.cn

表1. bean定义
属性 解释在……

spring-doc.cadn.net.cn

实例化Beanspring-doc.cadn.net.cn

名称spring-doc.cadn.net.cn

命名Beanspring-doc.cadn.net.cn

作用域spring-doc.cadn.net.cn

Bean 作用域spring-doc.cadn.net.cn

构造函数参数spring-doc.cadn.net.cn

依赖注入spring-doc.cadn.net.cn

属性spring-doc.cadn.net.cn

依赖注入spring-doc.cadn.net.cn

自动装配模式spring-doc.cadn.net.cn

自动装配协作对象spring-doc.cadn.net.cn

延迟初始化模式spring-doc.cadn.net.cn

延迟初始化的Beanspring-doc.cadn.net.cn

初始化方法spring-doc.cadn.net.cn

初始化回调spring-doc.cadn.net.cn

销毁方法spring-doc.cadn.net.cn

销毁回调spring-doc.cadn.net.cn

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

Bean 元数据和手动提供的单例实例需要尽可能早地进行注册,以便容器在自动连线和其他内省步骤中能够正确地处理它们。虽然在一定程度上支持覆盖现有的元数据和现有的单例实例,但运行时注册新 Bean(同时访问工厂)并不被正式支持,可能会导致并发访问异常、Bean 容器中的状态不一致,或两者皆有。spring-doc.cadn.net.cn

命名 Bean

每个bean有一个或多个标识符。这些标识符必须在托管该bean的容器内唯一。通常,一个bean只有一个标识符。然而,如果需要多个标识符,额外的标识符可以视为别名。spring-doc.cadn.net.cn

在基于XML的配置元数据中,您使用id属性、name属性或两者来指定Bean标识符。 id属性允许您指定一个id。通常,这些名称是字母数字的('myBean'、'someService'等),但也可以包含特殊字符。如果您想为Bean引入其他别名,还可以在name属性中指定它们,用逗号(,)、分号(;)或空格分隔。尽管id属性被定义为xsd:string类型,但容器会强制执行Beanid的唯一性,而不是由XML解析器强制执行。spring-doc.cadn.net.cn

您不需要为bean提供nameid。如果您没有显式提供nameid,容器会为该bean生成一个唯一名称。但是,如果您希望通过使用ref元素或Service Locator风格的查找来通过名称引用该bean,则必须提供一个名称。 不提供名称的动机与使用内部bean自动连线协作器有关。spring-doc.cadn.net.cn

Bean命名约定

约定是在命名 bean 时使用标准的 Java 实例字段命名约定。也就是说,bean 名称以小写字母开头,之后采用驼峰命名法。此类名称的示例包括 accountManager, accountService, userDao, loginController, 依此类推。spring-doc.cadn.net.cn

保持 bean 的命名一致可以使你的配置更易于阅读和理解。 此外,如果你使用 Spring AOP,在根据名称将建议应用于一组相关的 bean 时,这会带来很大帮助。spring-doc.cadn.net.cn

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

在 Bean 定义之外别名一个 Bean

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

在某些情况下,明确指定bean实际定义的别名并不总是足够,然而。有时需要为在其他地方定义的bean引入一个别名。这在配置被拆分到各个子系统的大型系统中很常见,每个子系统都有自己的对象定义集。在基于XML的配置元数据中,可以使用<alias/>元素来实现这一点。下面的示例展示了如何操作:spring-doc.cadn.net.cn

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

在这种情况下,名为 fromName 的 bean(在同一容器中)也可以在使用此别名定义后,被引用为 toNamespring-doc.cadn.net.cn

例如,子系统A的配置元数据可能通过名称subsystemA-dataSource引用一个DataSource。子系统B的配置元数据可能通过名称subsystemB-dataSource引用一个DataSource。当组合使用这两个子系统的主应用程序时,主应用程序通过名称myApp-dataSource引用DataSource。为了使这三个名称都引用同一个对象,可以将以下别名定义添加到配置元数据中:spring-doc.cadn.net.cn

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

现在每个组件和主应用程序都可以通过一个唯一且保证不会与其他定义冲突的名称(有效地创建一个命名空间)来引用dataSource,但它们引用的是同一个bean。spring-doc.cadn.net.cn

Java-configuration

如果您使用Java配置,可以使用 @Bean 注解来提供别名。 请参阅 使用 @Bean 注解 以获取详细信息。spring-doc.cadn.net.cn

实例化 Bean

一个 bean 定义本质上是创建一个或多个对象的配方。当容器被要求时,它会查看命名 bean 的配方,并使用该 bean 定义所封装的配置元数据来创建(或获取)实际的对象。spring-doc.cadn.net.cn

如果您使用基于XML的配置元数据,则在class属性中指定要实例化的对象的类型(或类)<bean/>元素。此class属性(内部上,是Class属性在BeanDefinition实例上)通常是必需的。(例外情况,请参见使用实例工厂方法进行实例化Bean定义继承。)您可以以两种方式使用Class属性:spring-doc.cadn.net.cn

  • 通常,在容器本身通过反射调用其构造函数直接创建 bean 的情况下,指定要构造的 bean 类,有点类似于带有 new 运算符的 Java 代码。spring-doc.cadn.net.cn

  • 要指定包含实际调用以创建对象的 static 工厂方法的类,在容器对类调用 static 工厂方法来创建 bean 的较少见情况下。从 static 工厂方法的调用中返回的对象类型可以是同一类,也可以是另一个类。spring-doc.cadn.net.cn

内部类名称

如果你想为一个嵌套类配置一个Bean定义,可以使用该嵌套类的二进制名称或源名称。spring-doc.cadn.net.cn

例如,如果您在 SomeThing 包中有一个名为 com.example 的类, 这个 SomeThing 类有一个名为 OtherThing 的内部类 static,它们可以通过美元符号 ($) 或点 (.) 分隔。因此,bean 定义中的 class 属性的值可以是 com.example.SomeThing$OtherThingcom.example.SomeThing.OtherThingspring-doc.cadn.net.cn

通过构造函数实例化

当您通过构造函数方法创建 bean 时,所有普通类都可以被 Spring 使用和兼容。也就是说,所开发的类不需要实现任何特定的接口,也不需要以特定的方式进行编码。只需指定 bean 类即可。然而,根据您为该特定 bean 使用的 IoC 类型,可能需要一个默认(空)构造函数。spring-doc.cadn.net.cn

Spring IoC 容器可以管理你希望它管理的几乎任何类。它不限于管理真正的 JavaBean。大多数 Spring 用户更倾向于使用实际的 JavaBean,这些 JavaBean 仅具有默认(无参数)构造函数,并且具有根据容器中的属性设计的适当 setter 和 getter 方法。你也可以在容器中使用更加奇特的非 bean 风格的类。例如,如果你需要使用一个完全不遵循 JavaBean 规范的旧版连接池,Spring 同样可以管理它。spring-doc.cadn.net.cn

使用基于 XML 的配置元数据,您可以按以下方式指定您的 Bean 类:spring-doc.cadn.net.cn

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

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

有关向构造函数提供参数(如需)以及在对象构造后设置对象实例属性的机制的详细信息,请参阅 注入依赖项spring-doc.cadn.net.cn

使用静态工厂方法实例化

在定义通过静态工厂方法创建的bean时,使用class属性来指定包含static工厂方法的类,并使用名为factory-method的属性来指定工厂方法本身的名称。您应该能够调用此方法(带有可选参数,如后面所述)并返回一个实际的对象,该对象随后会被当作通过构造函数创建的对象来处理。这种bean定义的一个用途是调用遗留代码中的static工厂。spring-doc.cadn.net.cn

以下 bean 定义指定了将通过调用工厂方法来创建 bean。该定义没有指定返回对象的类型(类),而是指定了包含工厂方法的类。在本例中,createInstance() 方法必须是一个 static 方法。下面的示例展示了如何指定工厂方法:spring-doc.cadn.net.cn

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

以下示例显示了一个将与前面的 bean 定义一起使用的类:spring-doc.cadn.net.cn

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()
		@JvmStatic
		fun createInstance() = clientService
	}
}

有关向工厂方法(可选)提供参数以及在对象从工厂返回后设置对象实例属性的机制的详细信息, 请参阅 详细依赖关系和配置spring-doc.cadn.net.cn

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

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

<!-- the factory bean, which contains a method called createClientServiceInstance() -->
<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"/>

以下示例显示了相应的类:spring-doc.cadn.net.cn

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

一个工厂类还可以包含多个工厂方法,如下例所示:spring-doc.cadn.net.cn

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

以下示例显示了相应的类:spring-doc.cadn.net.cn

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-doc.cadn.net.cn

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

确定 Bean 的运行时类型

确定特定bean的运行时类型并不总是显而易见的。在bean元数据定义中指定的类只是一个初始类引用,可能与声明的工厂方法结合使用,或者是FactoryBean类,这可能导致bean的不同运行时类型,或者在实例级工厂方法的情况下根本未设置(此时通过指定的factory-bean名称来解决)。 此外,AOP代理可能会使用基于接口的代理包装bean实例,仅暴露目标bean的实际类型(即其实现的接口)。spring-doc.cadn.net.cn

推荐的方法是通过指定的bean名称调用BeanFactory.getType来了解特定bean的实际运行时类型。这会考虑所有上述情况,并返回调用BeanFactory.getBean时对于同一bean名称将要返回的对象类型。spring-doc.cadn.net.cn