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

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

  • 包限定的类名:通常为 Bean 正在定义。

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

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

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

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

表 1.Bean 定义
财产 解释...

实例化 Bean

名字

命名 Bean

范围

Bean 范围

构造函数参数

依赖注入

性能

依赖注入

自动接线模式

自动接线协作者

延迟初始化模式

延迟初始化的 Bean

初始化方法

初始化回调

销毁方式

销毁回调

除了包含有关如何创建特定 bean,实现还允许注册现有的 在容器外部创建的对象(由用户创建)。这是通过访问 ApplicationContext 通过该方法,该方法返回 实现。 支持 此注册通过和方法。但是,典型的应用程序仅使用通过常规定义的 bean Bean 定义元数据。ApplicationContextBeanFactorygetBeanFactory()DefaultListableBeanFactoryDefaultListableBeanFactoryregisterSingleton(..)registerBeanDefinition(..)

Bean 元数据和手动提供的单例实例需要尽早注册 为了让容器在自动接线过程中正确地推理它们 和其他内省步骤。同时覆盖现有元数据和现有 在某种程度上支持单例实例,在 运行时(与工厂的实时访问同时进行)不受官方支持,并且可能 导致并发访问异常和/或 Bean 容器中的状态不一致。

覆盖 Bean

当使用标识符注册 Bean 时,将发生 Bean 覆盖 已分配。虽然 Bean 覆盖是可能的,但它使配置更加困难 阅读,此功能将在将来的版本中弃用。

要完全禁用 Bean 覆盖,可以在刷新之前将标志设置为 on。在这样的设置中,一个 如果使用 Bean 覆盖,则会引发异常。allowBeanDefinitionOverridingfalseApplicationContext

默认情况下,容器会记录每个 bean 在级别覆盖,以便您可以 相应地调整您的配置。虽然不建议这样做,但您可以静音这些日志 通过将标志设置为 。INFOallowBeanDefinitionOverridingtrue

Java 配置

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

命名 Bean

每个 Bean 都有一个或多个标识符。这些标识符在 托管 Bean 的容器。Bean 通常只有一个标识符。但是,如果它 需要多个,多余的可以被视为别名。

在基于 XML 的配置元数据中,使用属性、属性或 两者都指定 Bean 标识符。该属性允许您只指定一个 . 通常,这些名称是字母数字(“myBean”、“someService”等),但它们 也可以包含特殊字符。如果要为 bean,也可以在属性中指定它们,用逗号()分隔, 分号 () 或空格。尽管该属性被定义为一种类型,但 Bean 的唯一性是由容器强制执行的,而不是由 XML 强制执行的 解析 器。idnameididname,;idxsd:stringid

您不需要为 bean 提供 a 或 an。如果未显式提供 或,则容器将为该 Bean 生成唯一名称。然而 如果您想通过名称引用该 Bean,请通过使用 元素或 服务定位器样式查找时,必须提供名称。 不提供名称的动机与使用内部 bean自动连接协作者有关。nameidnameidref

Bean 命名约定

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

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

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

在 Bean 定义之外对 Bean 进行别名

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

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

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

在这种情况下,名为 bean (in the same container) 的 bean 也可以, 使用此别名定义后,称为 。fromNametoName

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

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

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

Java 配置

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

实例化 Bean

Bean 定义本质上是创建一个或多个对象的配方。这 容器在询问时查看命名 Bean 的配方并使用配置 由该 Bean 定义封装的元数据,用于创建(或获取)实际对象。

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

  • 通常,指定要构造的 Bean 类,其中容器 它本身通过反射性地调用其构造函数来直接创建 Bean,在某种程度上 等同于带有运算符的 Java 代码。new

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

嵌套类名

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

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

使用构造函数进行实例化

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

Spring IoC 容器几乎可以管理您希望它管理的任何类。是的 不仅限于管理真正的 JavaBeans。大多数 Spring 用户更喜欢实际的 JavaBeans 仅建模默认(无参数)构造函数和适当的 setter 和 getter 在容器中的属性之后。你也可以有更多异国情调的非豆式 容器中的类。例如,如果需要使用旧连接池 绝对不遵守 JavaBean 规范,Spring 可以将其管理为 井。

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

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

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

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

在构造函数参数的情况下,容器可以选择相应的 构造函数。也就是说,为了避免歧义, 建议使构造函数签名尽可能简单明了。

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

在定义使用静态工厂方法创建的 Bean 时,请使用该属性指定包含工厂方法和属性的类 named 指定工厂方法本身的名称。你应该是 能够调用此方法(使用可选参数,如后面所述)并返回一个 live 对象,随后将其视为通过构造函数创建的对象。 这种 Bean 定义的一个用途是在遗留代码中调用工厂。classstaticfactory-methodstatic

以下 Bean 定义指定将通过调用 工厂方法。定义未指定返回对象的类型(类), 而是包含工厂方法的类。在此示例中,该方法必须是方法。下面的示例演示如何 指定工厂方法:createInstance()static

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

下面的示例显示了一个可以使用前面的 Bean 定义的类:

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

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

在工厂方法参数的情况下,容器可以选择相应的 方法,在几个同名的重载方法中。也就是说,为了避免歧义, 建议使工厂方法签名尽可能简单明了。

工厂方法重载的一个典型问题案例是 Mockito,它有许多 方法的重载。选择最具体的变体:mockmock

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

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

与通过静态工厂方法实例化类似,使用实例工厂方法实例化调用非静态 方法从容器中获取现有 Bean 以创建新 Bean。要使用此内容,请执行此操作 mechanism,将属性留空,并在属性中 指定当前(或父或祖先)容器中 Bean 的名称,该容器包含 要调用以创建对象的实例方法。设置名称 factory 方法本身与属性。以下示例显示 如何配置这样的 Bean:classfactory-beanfactory-method

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

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

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

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

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

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

  • 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)。 请参阅依赖项和配置的详细信息

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

确定 Bean 的运行时类型

确定特定 Bean 的运行时类型并非易事。中的指定类 Bean 元数据定义只是一个初始类引用,可能会组合在一起 使用声明的工厂方法或作为可能导致 Bean 的运行时类型不同,或者在实例级别的情况下根本不设置 factory 方法(改为通过指定名称进行解析)。 此外,AOP 代理可能会使用基于接口的代理包装 Bean 实例,并带有 目标 Bean 的实际类型(仅其实现的接口)的有限曝光。FactoryBeanfactory-bean

要了解特定 Bean 的实际运行时类型,推荐的方法是 对指定 Bean 名称的调用。这需要以上所有条件 大小写,并返回调用的对象类型 将返回相同的 Bean 名称。BeanFactory.getTypeBeanFactory.getBean