Bean Overview

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

A Spring IoC container manages one or more beans. These beans are created with the configuration metadata that you supply to the container (for example, in the form of XML <bean/> definitions).

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

Within the container itself, these bean definitions are represented as BeanDefinition objects, which contain (among other information) the following metadata:

  • A package-qualified class name: typically, the actual implementation class of the bean being defined.

  • Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).

  • References to other beans that are needed for the bean to do its work. These references are also called collaborators or dependencies.

  • Other configuration settings to set in the newly created object — for example, the size limit of the pool or the number of connections to use in a bean that manages a connection pool.

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

This metadata translates to a set of properties that make up each bean definition. The following table describes these properties:

Table 1. The bean definition
Property Explained in…​

Class

Instantiating Beans

Name

Naming Beans

Scope

Bean Scopes

Constructor arguments

Dependency Injection

Properties

Dependency Injection

Autowiring mode

Autowiring Collaborators

Lazy initialization mode

Lazy-initialized Beans

Initialization method

Initialization Callbacks

Destruction method

Destruction Callbacks

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

In addition to bean definitions that contain information on how to create a specific bean, the ApplicationContext implementations also permit the registration of existing objects that are created outside the container (by users). This is done by accessing the ApplicationContext’s BeanFactory through the getBeanFactory() method, which returns the DefaultListableBeanFactory implementation. DefaultListableBeanFactory supports this registration through the registerSingleton(..) and registerBeanDefinition(..) methods. However, typical applications work solely with beans defined through regular bean definition metadata.

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

Bean metadata and manually supplied singleton instances need to be registered as early as possible, in order for the container to properly reason about them during autowiring and other introspection steps. While overriding existing metadata and existing singleton instances is supported to some degree, the registration of new beans at runtime (concurrently with live access to the factory) is not officially supported and may lead to concurrent access exceptions, inconsistent state in the bean container, or both.

Overriding Beans

当使用已分配的标识符注册 bean 时,就会发生 bean 覆盖。虽然 bean 覆盖是可能的,但它会使配置更难以阅读。

Bean overriding occurs when a bean is registered using an identifier that is already allocated. While bean overriding is possible, it makes the configuration harder to read.

Bean 重写将在未来版本中弃用。

Bean overriding will be deprecated in a future release.

若要完全禁用 Bean 覆盖,可以在刷新前将 ApplicationContext 上的 allowBeanDefinitionOverriding 标志设置为 false。在这种设置下,如果使用了 Bean 覆盖,则会抛出异常。

To disable bean overriding altogether, you can set the allowBeanDefinitionOverriding flag to false on the ApplicationContext before it is refreshed. In such a setup, an exception is thrown if bean overriding is used.

默认情况下,容器会以 INFO 级别记录每次尝试覆盖 Bean,以便你可以相应地调整配置。虽然不建议这样做,但你可以通过将 allowBeanDefinitionOverriding 标志设置为 true 来取消那些日志。

By default, the container logs every attempt to override a bean at INFO level so that you can adapt your configuration accordingly. While not recommended, you can silence those logs by setting the allowBeanDefinitionOverriding flag to true.

Java Configuration

如果你使用 Java 配置,则对应的 @Bean 方法始终会用具有相同组件名称的已扫描 Bean 类悄悄覆盖,只要 @Bean 方法的返回类型与该 Bean 类匹配即可。这仅仅意味着容器会调用 @Bean 工厂方法,而不是 Bean 类上的任何预先声明的构造函数。

If you use Java Configuration, a corresponding @Bean method always silently overrides a scanned bean class with the same component name as long as the return type of the @Bean method matches that bean class. This simply means that the container will call the @Bean factory method in favor of any pre-declared constructor on the bean class.

我们承认在测试场景中重写 Bean 很方便,Spring Framework 6.2 目前明确支持这一点。请参阅 this section 了解更多详情。

We acknowledge that overriding beans in test scenarios is convenient, and there is explicit support for this as of Spring Framework 6.2. Please refer to this section for more details.

Naming Beans

每个 Bean 有一个或多个标识符。这些标识符必须在容纳 Bean 的容器内唯一。通常一个 Bean 只有一个标识符。但是,如果要求有多个,则可以将额外的标识符视为别名。

Every bean has one or more identifiers. These identifiers must be unique within the container that hosts the bean. A bean usually has only one identifier. However, if it requires more than one, the extra ones can be considered aliases.

在基于 XML 的配置元数据中,你可以使用 id 属性、name 属性或两者来指定 Bean 标识符。id 属性允许你确切地指定一个 id。按照惯例,这些名称是字母数字型的(“myBean”、“someService”等),但它们也可以包含特殊字符。如果你想为 Bean 引入其他别名,你也可以在 name 属性中指定这些别名,并用逗号 (,)、分号 (;) 或空格分隔。尽管 id 属性被定义为 xsd:string 类型,但 Bean id 的惟一性是由容器强制的,而不是由 XML 解析器强制的。

In XML-based configuration metadata, you use the id attribute, the name attribute, or both to specify bean identifiers. The id attribute lets you specify exactly one id. Conventionally, these names are alphanumeric ('myBean', 'someService', etc.), but they can contain special characters as well. If you want to introduce other aliases for the bean, you can also specify them in the name attribute, separated by a comma (,), semicolon (;), or white space. Although the id attribute is defined as an xsd:string type, bean id uniqueness is enforced by the container, though not by XML parsers.

你不必提供一个 name 或一个 id 给一个 Bean。如果你没有明确提供 nameid,容器会为该 Bean 生成一个唯一名称。但是,如果你想通过使用 ref 元素或 Service Locator 样式查找来按名称引用该 Bean,你必须提供一个名称。不提供名称的动机与使用 inner beansautowiring collaborators 有关。

You are not required to supply a name or an id for a bean. If you do not supply a name or id explicitly, the container generates a unique name for that bean. However, if you want to refer to that bean by name, through the use of the ref element or a Service Locator style lookup, you must provide a name. Motivations for not supplying a name are related to using inner beans and autowiring collaborators.

Bean Naming Conventions

命名 Bean 时,惯例是使用标准 Java 约定用于实例字段名称。也就是说,Bean 名称以小写字母开头,从该字母开始采用驼峰式大小写。此类名称的示例包括 accountManageraccountServiceuserDaologinController 等等。

The convention is to use the standard Java convention for instance field names when naming beans. That is, bean names start with a lowercase letter and are camel-cased from there. Examples of such names include accountManager, accountService, userDao, loginController, and so forth.

一致地为 Bean 命名可以使你的配置更易于阅读和理解。此外,如果你使用 Spring AOP,在对一组按名称相关的 Bean 应用建议时,它会有很大帮助。

Naming beans consistently makes your configuration easier to read and understand. Also, if you use Spring AOP, it helps a lot when applying advice to a set of beans related by name.

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

With component scanning in the classpath, Spring generates bean names for unnamed components, following the rules described earlier: essentially, taking the simple class name and turning its initial character to lower-case. However, in the (unusual) special case when there is more than one character and both the first and second characters are upper case, the original casing gets preserved. These are the same rules as defined by java.beans.Introspector.decapitalize (which Spring uses here).

Aliasing a Bean outside the Bean Definition

在 Bean 定义本身中,你可以通过将 id 属性指定的一个名称与 name 属性中的其他名称组合起来,为 Bean 提供多个名称。这些名称可以作为同个 Bean 的等效别名,并且在某些情况下很有用,例如让应用程序中的每个组件都可以使用特定于该组件本身的 Bean 名称来引用一个公共依赖项。

In a bean definition itself, you can supply more than one name for the bean, by using a combination of up to one name specified by the id attribute and any number of other names in the name attribute. These names can be equivalent aliases to the same bean and are useful for some situations, such as letting each component in an application refer to a common dependency by using a bean name that is specific to that component itself.

但是,仅在 Bean 实际定义的位置指定所有别名并不总是足够。有时需要为在其他地方定义的 Bean 引入别名。这种情况通常发生在大型系统中,其中配置在每个子系统中拆分,每个子系统都有自己的一组对象定义。在基于 XML 的配置元数据中,你可以使用 <alias/> 元素来实现这一点。以下示例显示如何进行此操作:

Specifying all aliases where the bean is actually defined is not always adequate, however. It is sometimes desirable to introduce an alias for a bean that is defined elsewhere. This is commonly the case in large systems where configuration is split amongst each subsystem, with each subsystem having its own set of object definitions. In XML-based configuration metadata, you can use the <alias/> element to accomplish this. The following example shows how to do so:

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

在这种情况下,(在同一个容器中)名为 fromName 的 Bean 也可能在使用此别名定义后被称为 toName

In this case, a bean (in the same container) named fromName may also, after the use of this alias definition, be referred to as toName.

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

For example, the configuration metadata for subsystem A may refer to a DataSource by the name of subsystemA-dataSource. The configuration metadata for subsystem B may refer to a DataSource by the name of subsystemB-dataSource. When composing the main application that uses both these subsystems, the main application refers to the DataSource by the name of myApp-dataSource. To have all three names refer to the same object, you can add the following alias definitions to the configuration metadata:

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

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

Now each component and the main application can refer to the dataSource through a name that is unique and guaranteed not to clash with any other definition (effectively creating a namespace), yet they refer to the same bean.

Java-configuration

如果您使用 Java 配置,可以使用 @Bean`注释来提供别名。详细信息,请参阅 Using the `@Bean Annotation

If you use Java Configuration, the @Bean annotation can be used to provide aliases. See Using the @Bean Annotation for details.

Instantiating Beans

Bean 定义实质上是创建一或多个对象的一个秘诀。在被询问时,容器会查看已命名 Bean 的秘诀并使用该 Bean 定义封送的配置元数据来创建(或获取)一个实际对象。

A bean definition is essentially a recipe for creating one or more objects. The container looks at the recipe for a named bean when asked and uses the configuration metadata encapsulated by that bean definition to create (or acquire) an actual object.

如果你使用基于 XML 的配置元数据,则可以在 <bean/> 元素的 class 属性中指定要实例化的对象类型(或类)。这个 class 属性(在内部是 BeanDefinition 实例上的 Class 属性)通常是必需的。(有关异常,请参阅 Instantiation by Using an Instance Factory MethodBean Definition Inheritance。)你可以通过以下两种方式之一使用 Class 属性:

If you use XML-based configuration metadata, you specify the type (or class) of object that is to be instantiated in the class attribute of the <bean/> element. This class attribute (which, internally, is a Class property on a BeanDefinition instance) is usually mandatory. (For exceptions, see Instantiation by Using an Instance Factory Method and Bean Definition Inheritance.) You can use the Class property in one of two ways:

  • Typically, to specify the bean class to be constructed in the case where the container itself directly creates the bean by calling its constructor reflectively, somewhat equivalent to Java code with the new operator.

  • To specify the actual class containing the static factory method that is invoked to create the object, in the less common case where the container invokes a static factory method on a class to create the bean. The object type returned from the invocation of the static factory method may be the same class or another class entirely.

Nested class names

如果你想配置嵌套类的 Bean 定义,则可以使用嵌套类的二进制名称或源名称。

If you want to configure a bean definition for a nested class, you may use either the binary name or the source name of the nested class.

例如,如果你在 com.example 包中有一个名为 SomeThing 的类,并且这个 SomeThing 类有一个名为 OtherThingstatic 嵌套类,则它们可以使用美元符号($)或圆点(.)分隔。因此,Bean 定义中 class 属性的值将是 com.example.SomeThing$OtherThingcom.example.SomeThing.OtherThing

For example, if you have a class called SomeThing in the com.example package, and this SomeThing class has a static nested class called OtherThing, they can be separated by a dollar sign ($) or a dot (.). So the value of the class attribute in a bean definition would be com.example.SomeThing$OtherThing or com.example.SomeThing.OtherThing.

Instantiation with a Constructor

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

When you create a bean by the constructor approach, all normal classes are usable by and compatible with Spring. That is, the class being developed does not need to implement any specific interfaces or to be coded in a specific fashion. Simply specifying the bean class should suffice. However, depending on what type of IoC you use for that specific bean, you may need a default (empty) constructor.

Spring IoC 容器几乎可以管理你希望它管理的任何类。它不限于管理真正的 JavaBean。大多数 Spring 用户更喜欢仅具有默认(无参数)构造函数和适当的、根据容器中属性建模的 setter 和 getter 的实际 JavaBean。你还可以让容器里更多一些非 bean 风格的类。例如,如果你需要使用绝对不遵守 JavaBean 规范的旧连接池,Spring 也可以管理它。

The Spring IoC container can manage virtually any class you want it to manage. It is not limited to managing true JavaBeans. Most Spring users prefer actual JavaBeans with only a default (no-argument) constructor and appropriate setters and getters modeled after the properties in the container. You can also have more exotic non-bean-style classes in your container. If, for example, you need to use a legacy connection pool that absolutely does not adhere to the JavaBean specification, Spring can manage it as well.

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

With XML-based configuration metadata you can specify your bean class as follows:

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

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

有关在构造对象后向构造函数提供参数(如果必需)以及设置对象实例属性的机制的详细信息,请参阅 Injecting Dependencies

For details about the mechanism for supplying arguments to the constructor (if required) and setting object instance properties after the object is constructed, see Injecting Dependencies.

在构造函数参数的情况下,容器可以在多个重载构造函数中选择一个相应的构造函数。也就是说,为避免歧义,建议尽可能地让构造函数签名直接。

In the case of constructor arguments, the container can select a corresponding constructor among several overloaded constructors. That said, to avoid ambiguities, it is recommended to keep your constructor signatures as straightforward as possible.

Instantiation with a Static Factory Method

当使用静态工厂方法定义 bean 时,请使用 class 属性指定包含 static 工厂方法的类,并使用名为 factory-method 的特性指定工厂方法本身的名称。你应该能够调用此方法(如有需要并提供可选参数),并返回一个实况对象,该对象随后将被视为通过构造函数创建的对象。此类 bean 定义的一个用途是调用旧代码中的 static 工厂。

When defining a bean that you create with a static factory method, use the class attribute to specify the class that contains the static factory method and an attribute named factory-method to specify the name of the factory method itself. You should be able to call this method (with optional arguments, as described later) and return a live object, which subsequently is treated as if it had been created through a constructor. One use for such a bean definition is to call static factories in legacy code.

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

The following bean definition specifies that the bean will be created by calling a factory method. The definition does not specify the type (class) of the returned object, but rather the class containing the factory method. In this example, the createInstance() method must be a static method. The following example shows how to specify a factory method:

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

以下示例显示将使用前面的 bean 定义的类:

The following example shows a class that would work with the preceding bean definition:

  • Java

  • Kotlin

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

有关向工厂方法提供(可选)参数以及在从工厂返回对象后设置对象实例属性的机制的详细信息,请参阅 Dependencies and Configuration in Detail

For details about the mechanism for supplying (optional) arguments to the factory method and setting object instance properties after the object is returned from the factory, see Dependencies and Configuration in Detail.

对于工厂方法参数来说,容器可以在同一名称的几个重载方法中选择一个相应的方法。也就是说,为了避免歧义,建议尽可能保持工厂方法签名简洁明了。

In the case of factory method arguments, the container can select a corresponding method among several overloaded methods of the same name. That said, to avoid ambiguities, it is recommended to keep your factory method signatures as straightforward as possible.

工厂方法重载的一个常见问题是 Mockito 及其 mock 方法的许多重载。选择 mock 可能的最具体的变体:

A typical problematic case with factory method overloading is Mockito with its many overloads of the mock method. Choose the most specific variant of mock possible:

<bean id="clientService" class="org.mockito.Mockito" factory-method="mock">
	<constructor-arg type="java.lang.Class" value="examples.ClientService"/>
	<constructor-arg type="java.lang.String" value="clientService"/>
</bean>

Instantiation by Using an Instance Factory Method

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

Similar to instantiation through a static factory method , instantiation with an instance factory method invokes a non-static method of an existing bean from the container to create a new bean. To use this mechanism, leave the class attribute empty and, in the factory-bean attribute, specify the name of a bean in the current (or parent or ancestor) container that contains the instance method that is to be invoked to create the object. Set the name of the factory method itself with the factory-method attribute. The following example shows how to configure such a bean:

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

以下示例显示相应的类:

The following example shows the corresponding class:

  • Java

  • Kotlin

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

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

One factory class can also hold more than one factory method, as the following example shows:

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

以下示例显示相应的类:

The following example shows the corresponding class:

  • Java

  • Kotlin

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) 进行管理和配置。请参阅 Dependencies and Configuration in Detail

This approach shows that the factory bean itself can be managed and configured through dependency injection (DI). See Dependencies and Configuration in Detail.

在 Spring 文档中,“工厂 Bean”是指已配置在 Spring 容器中且可通过 instancestatic 工厂方法创建对象的 Bean。相比之下,FactoryBean(注意大写)是指 Spring 特定的 FactoryBean 实现类。

In Spring documentation, "factory bean" refers to a bean that is configured in the Spring container and that creates objects through an instance or static factory method. By contrast, FactoryBean (notice the capitalization) refers to a Spring-specific FactoryBean implementation class.

Determining a Bean’s Runtime Type

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

The runtime type of a specific bean is non-trivial to determine. A specified class in the bean metadata definition is just an initial class reference, potentially combined with a declared factory method or being a FactoryBean class which may lead to a different runtime type of the bean, or not being set at all in case of an instance-level factory method (which is resolved via the specified factory-bean name instead). Additionally, AOP proxying may wrap a bean instance with an interface-based proxy with limited exposure of the target bean’s actual type (just its implemented interfaces).

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

The recommended way to find out about the actual runtime type of a particular bean is a BeanFactory.getType call for the specified bean name. This takes all of the above cases into account and returns the type of object that a BeanFactory.getBean call is going to return for the same bean name.