1.4. 依赖

预计阅读时间: 66 分钟

一个典型的企业应用程序并不只包含一个对象(或 Spring 术语中的 bean )。即使是最简单的应用程序也包含几个协同工作的对象,以向最终用户展示一个连贯的应用程序。 本节将解释你如何从定义一系列独立的 bean 定义过渡到一个完全实现的应用程序,其中对象协作以实现目标。

1.4.1. 依赖注入

依赖注入(DI)是一种过程,其中对象仅通过构造函数参数、工厂方法的参数或在对象实例构建或从工厂方法返回后设置的属性来定义它们的依赖关系(即它们与之协同工作的其他对象)。 容器在创建 bean 时注入这些依赖项。 从根本上说,这一过程是 bean 本身通过直接构建类或服务定位器模式控制其依赖关系的实例化或位置的逆过程(因此被称为 "控制反转")。

代码遵循 DI 原则时更简洁,当对象被提供其依赖项时,解耦更有效。对象不会查找其依赖项,也不知道依赖项的位置或类。因此,你的类更容易进行测试,尤其是在依赖接口或抽象基类时,这允许在单元测试中使用存根或模拟实现。

DI 存在两种主要变体:基于构造函数的依赖注入基于 Setter 的依赖注入

基于构造函数的依赖注入

构造函数式依赖注入(Constructor-based DI)是通过容器调用一个带有多个参数的构造函数来实现的,每个参数都表示一个依赖项。使用带有特定参数的 static 工厂方法来构造 Bean 的方式与此几乎相同,因此这里将构造函数和 static 工厂方法的参数视为相似处理。以下示例展示了一个只能通过构造函数注入进行依赖注入的类:

public class SimpleMovieLister {

    // SimpleMovieLister 依赖于 MovieFinder
    private final MovieFinder movieFinder;

    // 构造函数,以便 Spring 容器可以注入 MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
    this.movieFinder = movieFinder;
    }
    // 实际使用注入的 MovieFinder 的业务逻辑被省略...
}
    // 构造函数,以便 Spring 容器可以注入 MovieFinder
class SimpleMovieLister(private val movieFinder: MovieFinder) {
    // 实际使用注入的 MovieFinder 的业务逻辑被省略...
}

注意,这个类没有特别之处。它是一个没有对容器特定接口、基类或注解有依赖的 POJO。

构造函数参数解析 {#beans-factory-ctor-arguments-resolution

构造函数参数解析匹配是通过使用参数的类型来实现的。如果 bean 定义中的构造函数参数不存在潜在的歧义,那么 bean 定义中定义构造函数参数的顺序就是在实例化 bean 时将这些参数提供给相应构造函数的顺序。 请看下面这个类:

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
        // ...
    }
}
package x.y

class ThingOne(thingTwo: ThingTwo, thingThree: ThingThree)

假设 ThingTwoThingThree 类之间没有继承关系,因此不存在潜在的歧义。由此,以下配置可以正常工作,而且在 <constructor-arg/> 元素中无需显式指定构造函数参数的索引或类型。


<beans>
  <bean id="beanOne" class="x.y.ThingOne">
    <constructor-arg ref="beanTwo"/>
    <constructor-arg ref="beanThree"/>
  </bean>

  <bean id="beanTwo" class="x.y.ThingTwo"/>

  <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

当引用另一个 Bean 时,类型是已知的,因此可以进行匹配(正如前面的示例所示)。然而,当使用简单类型(例如 <value>true</value> )时,Spring 无法确定值的类型,因此在没有额外信息的情况下无法通过类型进行匹配。请参考以下类:

package examples;

public class ExampleBean {

    // 计算终极答案所需的年份数量
    private final int years;

    // 生命、宇宙和万物的答案
    private final String ultimateAnswer;

    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
package examples

class ExampleBean(
    private val years: Int, // 算终极答案所需的年份数量
    private val ultimateAnswer: String // 生命、宇宙和万物的答案
)

构造函数参数类型匹配

在上述场景中,如果通过使用 type 属性显式指定构造函数参数的类型,容器就可以对简单类型使用类型匹配。如下示例所示:


<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg type="int" value="7500000"/>
  <constructor-arg type="java.lang.String" value="42"/>
</bean>

构造函数参数索引

你可以使用 index 属性来明确指定构造函数参数的索引,如下例所示:


<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg index="0" value="7500000"/>
  <constructor-arg index="1" value="42"/>
</bean>

除了解决多个简单值的歧义之外,指定索引还可以解决当构造函数具有两个相同类型的参数时的歧义。

TIP

索引是从 0 开始的。

构造函数参数名称

你也可以使用构造函数参数名称来进行值的区分,以下示例展示了这一点:


<bean id="exampleBean" class="examples.ExampleBean">
  <constructor-arg name="years" value="7500000"/>
  <constructor-arg name="ultimateAnswer" value="42"/>
</bean>

请记住,为了让这个功能开箱即用,你的代码必须在启用调试标志的情况下进行编译,这样 Spring 才能从构造函数中查找参数名称。如果你无法或者不想在编译时启用调试标志,你可以使用 @ConstructorProperties JDK 注解来显式命名构造函数的参数。示例类则需要如下所示:

package examples;

public class ExampleBean {

    // Fields omitted

    @ConstructorProperties({"years", "ultimateAnswer"})
    public ExampleBean(int years, String ultimateAnswer) {
        this.years = years;
        this.ultimateAnswer = ultimateAnswer;
    }
}
package examples

class ExampleBean
@ConstructorProperties("years", "ultimateAnswer")
constructor(val years: Int, val ultimateAnswer: String)

基于 Setter 的依赖注入

基于 Setter 的依赖注入是通过容器在调用无参数构造函数或无参数 static 工厂方法实例化你的 bean 后,调用你的 bean 的 setter 方法来实现的。

下面的示例显示了一个只能通过使用纯 setter 注入进行依赖注入的类。这个类是传统的 Java 类。它是一个 POJO ,对容器特定的接口、基类或注释没有依赖关系。

public class SimpleMovieLister {

  // SimpleMoviLister 依赖于 MoviFinder
  private MovieFinder movieFinder;
  
  // 一个 setter 方法,以便 Spring 容器可以注入 MoviFinder
  public void setMovieFinder(MovieFinder movieFinder) {
    this.movieFinder = movieFinder;
  }
  
  // 省略了实际使用注入的 MovieFinder 的业务逻辑...
}
class SimpleMovieLister {

// 后期初始化的属性,以便 Spring容 器可以注入 MovieFinder
lateinit var movieFinder: MovieFinder

// 省略了实际使用注入的 MovieFinder 的业务逻辑...
}

The ApplicationContext supports constructor-based and setter-based DI for the beans it manages. It also supports setter-based DI after some dependencies have already been injected through the constructor approach. You configure the dependencies in the form of a BeanDefinition, which you use in conjunction with PropertyEditor instances to convert properties from one format to another. However, most Spring users do not work with these classes directly (that is, programmatically) but rather with XML bean definitions, annotated components (that is, classes annotated with @Component, @Controller, and so forth), or @Bean methods in Java-based @Configuration classes. These sources are then converted internally into instances of BeanDefinition and used to load an entire Spring IoC container instance. The ApplicationContext supports constructor-based and setter-based DI for the beans it manages. It also supports setter-based DI after some dependencies have already been injected through the constructor approach. You configure the dependencies in the form of a BeanDefinition , which you use in conjunction with PropertyEditor instances to convert properties from one format to another. However, most Spring users do not work with these classes directly (that is, programmatically) but rather with XML bean definitions, annotated components (that is, classes annotated with @Component , @Controller , and so forth), or @Bean methods in Java-based @Configuration classes. These sources are then converted internally into instances of BeanDefinition and used to load an entire Spring IoC container instance.

(#beans-dependency-resolution)Dependency Resolution Process

依赖关系解析过程

The container performs bean dependency resolution as follows: 容器按照以下方式执行 Bean 依赖解析:

  • The ApplicationContext is created and initialized with configuration metadata that describes all the beans. Configuration metadata can be specified by XML, Java code, or annotations. ApplicationContext 是用描述所有 bean 的配置元数据创建和初始化的。配置元数据可以通过 XML、Java 代码或注解来指定。

  • For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method (if you use that instead of a normal constructor). These dependencies are provided to the bean, when the bean is actually created. 对于每个 Bean,其依赖以属性、构造函数参数或静态工厂方法参数(如果你使用静态工厂方法而不是普通构造函数)的形式表示。这些依赖在 Bean 实际创建时提供给 Bean。

  • Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container. 每个属性或构造函数参数是对要设置的值的实际定义,或是对容器中另一个 bean 的引用。

  • Each property or constructor argument that is a value is converted from its specified format to the actual type of that property or constructor argument. By default, Spring can convert a value supplied in string format to all built-in types, such as int, long, String, boolean, and so forth. 每个属性或构造函数参数的值都会从其指定的格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring 可以将以字符串格式提供的值转换为所有内置类型,例如 intlongStringboolean 等等。

The Spring container validates the configuration of each bean as the container is created. However, the bean properties themselves are not set until the bean is actually created. Spring 容器在创建容器时验证每个 bean 的配置。然而,bean 属性本身只有在 bean 实际创建时才会设置。 Beans that are singleton-scoped and set to be pre-instantiated (the default) are created when the container is created. Scopes are defined in Bean Scopes. Otherwise, the bean is created only when it is requested. Creation of a bean potentially causes a graph of beans to be created, as the bean’s dependencies and its dependencies' dependencies (and so on) are created and assigned. 单例作用域且设置为预实例化的(默认)的豆子在容器创建时创建。作用域在 Bean 作用域中定义。否则,只有在请求时才会创建豆子。创建豆子可能会创建一个豆子图,因为创建了豆子的依赖及其依赖的依赖(等等)并分配了它们。 Note that resolution mismatches among those dependencies may show up late — that is, on first creation of the affected bean. 请注意,这些依赖项之间的分辨率不匹配可能会晚些时候出现——即在受影响 bean 首次创建时。

You can generally trust Spring to do the right thing. It detects configuration problems, such as references to non-existent beans and circular dependencies, at container load-time. 通常你可以信任 Spring 做正确的事情。它在容器加载时检测配置问题,例如对不存在 bean 的引用和循环依赖。 Spring sets properties and resolves dependencies as late as possible, when the bean is actually created. 春季尽可能晚地设置属性和解析依赖,当 bean 实际创建时。 This means that a Spring container that has loaded correctly can later generate an exception when you request an object if there is a problem creating that object or one of its dependencies — for example, the bean throws an exception as a result of a missing or invalid property. 这意味着一个加载正确的 Spring 容器在请求对象时可能会抛出异常,如果创建该对象或其依赖项出现问题——例如,由于缺少或无效的属性,bean 抛出异常。 This potentially delayed visibility of some configuration issues is why ApplicationContext implementations by default pre-instantiate singleton beans. At the cost of some upfront time and memory to create these beans before they are actually needed, you discover configuration issues when the ApplicationContext is created, not later. You can still override this default behavior so that singleton beans initialize lazily, rather than being eagerly pre-instantiated. 这种某些配置问题可能延迟可见性是为什么默认情况下 ApplicationContext 实现预先实例化单例 bean 的原因。在创建这些 bean 之前,你将花费一些时间和内存,当创建 ApplicationContext 时,你就可以发现配置问题,而不是之后。你仍然可以覆盖这种默认行为,使单例 bean 延迟初始化,而不是预先实例化。

If no circular dependencies exist, when one or more collaborating beans are being injected into a dependent bean, each collaborating bean is totally configured prior to being injected into the dependent bean. 如果没有循环依赖存在,当将一个或多个协作的 bean 注入到依赖 bean 中时,每个协作 bean 在注入到依赖 bean 之前都将完全配置。 This means that, if bean A has a dependency on bean B, the Spring IoC container completely configures bean B prior to invoking the setter method on bean A. 这意味着,如果 Bean A 依赖于 Bean B,Spring IoC 容器在调用 Bean A 的 setter 方法之前,将完全配置 Bean B。 In other words, the bean is instantiated (if it is not a pre-instantiated singleton), its dependencies are set, and the relevant lifecycle methods (such as a configured init method or the InitializingBean callback method) are invoked. 换句话说,如果该对象不是预先实例化的单例,则实例化该对象,设置其依赖项,并调用相关的生命周期方法(如配置的初始化方法或 InitializingBean 回调方法)。

(#beans-some-examples)Examples of Dependency Injection

依赖注入示例

The following example uses XML-based configuration metadata for setter-based DI. A small part of a Spring XML configuration file specifies some bean definitions as follows: 以下示例使用基于 XML 的配置元数据进行基于 setter 的依赖注入。Spring XML 配置文件的一部分指定了一些 bean 定义如下:

<bean id="exampleBean" class="examples.ExampleBean"> <!-- setter injection using the nested ref element --> <property name="beanOne"> <ref bean="anotherExampleBean"/> </property> <!-- setter injection using the neater ref attribute --> <property name="beanTwo" ref="yetAnotherBean"/> <property name="integerProperty" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

The following example shows the corresponding ExampleBean class: 以下示例显示了相应的 ExampleBean 类:

public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; } public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; } public void setIntegerProperty(int i) { this.i = i; } }
class ExampleBean { lateinit var beanOne: AnotherBean lateinit var beanTwo: YetAnotherBean var i: Int = 0 }

In the preceding example, setters are declared to match against the properties specified in the XML file. The following example uses constructor-based DI: 在前面示例中,setter 被声明以匹配 XML 文件中指定的属性。以下示例使用基于构造函数的依赖注入:

<bean id="exampleBean" class="examples.ExampleBean"> <!-- constructor injection using the nested ref element --> <constructor-arg> <ref bean="anotherExampleBean"/> </constructor-arg> <!-- constructor injection using the neater ref attribute --> <constructor-arg ref="yetAnotherBean"/> <constructor-arg type="int" value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

The following example shows the corresponding ExampleBean class: 以下示例显示了相应的 ExampleBean 类:

public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { this.beanOne = anotherBean; this.beanTwo = yetAnotherBean; this.i = i; } }
class ExampleBean( private val beanOne: AnotherBean, private val beanTwo: YetAnotherBean, private val i: Int)

The constructor arguments specified in the bean definition are used as arguments to the constructor of the ExampleBean. 构造器参数在 bean 定义中指定,用作 ExampleBean 构造器的参数。

Now consider a variant of this example, where, instead of using a constructor, Spring is told to call a static factory method to return an instance of the object: 现在考虑这个示例的一个变体,在这个变体中,不是使用构造函数,而是告诉 Spring 调用一个 static 工厂方法来返回对象的实例:

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance"> <constructor-arg ref="anotherExampleBean"/> <constructor-arg ref="yetAnotherBean"/> <constructor-arg value="1"/> </bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/> <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

The following example shows the corresponding ExampleBean class: 以下示例显示了相应的 ExampleBean 类:

public class ExampleBean { // a private constructor private ExampleBean(...) { ... } // a static factory method; the arguments to this method can be // considered the dependencies of the bean that is returned, // regardless of how those arguments are actually used. public static ExampleBean createInstance ( AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...); // some other operations... return eb; } }
class ExampleBean private constructor() { companion object { // a static factory method; the arguments to this method can be // considered the dependencies of the bean that is returned, // regardless of how those arguments are actually used. @JvmStatic fun createInstance(anotherBean: AnotherBean, yetAnotherBean: YetAnotherBean, i: Int): ExampleBean { val eb = ExampleBean (...) // some other operations... return eb } } }

Arguments to the static factory method are supplied by <constructor-arg/> elements, exactly the same as if a constructor had actually been used. The type of the class being returned by the factory method does not have to be of the same type as the class that contains the static factory method (although, in this example, it is). An instance ( non-static) factory method can be used in an essentially identical fashion (aside from the use of the factory-bean attribute instead of the class attribute), so we do not discuss those details here. 工厂方法的参数由 <constructor-arg/> 元素提供,与实际使用构造函数时完全相同。工厂方法返回的类的类型不必与包含 static 工厂方法的类相同(尽管在这个例子中是)。实例(非静态)工厂方法可以以基本相同的方式使用(除了使用 factory-bean 属性而不是 class 属性之外),因此我们在这里不讨论这些细节。

(#beans-factory-properties-detailed)1.4.2. Dependencies and Configuration in Detail

1.4.2. 依赖项和详细配置

As mentioned in the previous section, you can define bean properties and constructor arguments as references to other managed beans (collaborators) or as values defined inline. Spring’s XML-based configuration metadata supports sub-element types within its <property/> and <constructor-arg/> elements for this purpose. 如前文所述,你可以将 bean 属性和构造函数参数定义为对其他管理 bean(协作者)的引用或定义为内联定义的值。基于 XML 的 Spring 配置元数据支持在其 <property/><constructor-arg/> 元素内使用子元素类型来实现此目的。

(#beans-value-element)Straight Values (Primitives, Strings, and so on)

直接值(原始类型、字符串等)

The value attribute of the <property/> element specifies a property or constructor argument as a human-readable string representation. Spring’s conversion service is used to convert these values from a String to the actual type of the property or argument. The following example shows various values being set: value 属性指定了作为可读字符串表示形式的属性或构造函数参数。Spring 的转换服务用于将这些值从 String 转换为属性或参数的实际类型。以下示例显示了设置的各种值:

<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- results in a setDriverClassName(String) call --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="misterkaoli"/> </bean>

The following example uses the p-namespace for even more succinct XML configuration: 以下示例使用 p-命名空间以实现更简洁的 XML 配置:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/mydb" p:username="root" p:password="misterkaoli"/> </beans>

The preceding XML is more succinct. However, typos are discovered at runtime rather than design time, unless you use an IDE (such as IntelliJ IDEA or the Spring Tools for Eclipse) that supports automatic property completion when you create bean definitions. Such IDE assistance is highly recommended. 前述 XML 更简洁。然而,错误是在运行时而不是在设计时被发现的,除非你使用支持在创建 bean 定义时自动属性完成的 IDE(如 IntelliJ IDEA 或 Spring Tools for Eclipse)。这种 IDE 辅助功能非常推荐。

You can also configure a java.util.Properties instance, as follows: 你也可以配置一个 java.util.Properties 实例,如下所示:

<bean id="mappings" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <!-- typed as a java.util.Properties --> <property name="properties"> <value> jdbc.driver.className=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/mydb </value> </property> </bean>

The Spring container converts the text inside the <value/> element into a java.util.Properties instance by using the JavaBeans PropertyEditor mechanism. This is a nice shortcut, and is one of a few places where the Spring team do favor the use of the nested <value/> element over the value attribute style.

(#beans-idref-element)Theidref elementidref 元素

The idref element is simply an error-proof way to pass the id (a string value - not a reference) of another bean in the container to a <constructor-arg/> or <property/> element. The following example shows how to use it: idref 元素简单来说是一种防止出错的传递方式,将容器中另一个 bean 的 id (一个字符串值 - 不是引用)传递给 <constructor-arg/><property/> 元素。以下示例展示了如何使用它:

<bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="..."> <property name="targetName"> <idref bean="theTargetBean"/> </property> </bean>

The preceding bean definition snippet is exactly equivalent (at runtime) to the following snippet: 前述的 Bean 定义片段在运行时与以下片段完全等价:

<bean id="theTargetBean" class="..."/> <bean id="client" class="..."> <property name="targetName" value="theTargetBean"/> </bean>

The first form is preferable to the second, because using the idref tag lets the container validate at deployment time that the referenced, named bean actually exists. In the second variation, no validation is performed on the value that is passed to the targetName property of the client bean. Typos are only discovered (with most likely fatal results) when the client bean is actually instantiated. If the client bean is a prototype bean, this typo and the resulting exception may only be discovered long after the container is deployed. 第一种形式比第二种更可取,因为使用 idref 标签可以让容器在部署时验证所引用的命名 bean 实际上是否存在。在第二种变化中,不会对传递给 client bean 的 targetName 属性的值进行验证。只有在 client bean 实际实例化时,才会发现这些错误(可能造成致命后果)。如果 client bean 是一个原型 bean,那么这个错误和由此产生的异常可能只有在容器部署很久之后才会被发现。

The local attribute on the idref element is no longer supported in the 4.0 beans XSD, since it does not provide value over a regular bean reference any more. Change your existing idref local references to idref bean when upgrading to the 4.0 schema. local 属性在 4.0 版本的 beans XSD 中不再受支持,因为它不再比常规的 bean 引用提供任何价值。在升级到 4.0 架构时,请将现有的 idref local 引用更改为 idref bean

A common place (at least in versions earlier than Spring 2.0) where the <idref/> element brings value is in the configuration of AOP interceptors in a ProxyFactoryBean bean definition. Using <idref/> elements when you specify the interceptor names prevents you from misspelling an interceptor ID. 一个常见的位置(至少在 Spring 2.0 之前的版本中) <idref/> 元素带来价值的地方是在 ProxyFactoryBean bean 定义中配置 AOP 拦截器。当你指定拦截器名称时使用 <idref/> 元素可以防止你拼写拦截器 ID 错误。

(#beans-ref-element)References to Other Beans (Collaborators)

其他豆子(合作者)的引用

The ref element is the final element inside a <constructor-arg/> or <property/> definition element. Here, you set the value of the specified property of a bean to be a reference to another bean (a collaborator) managed by the container. ref 元素是 <constructor-arg/><property/> 定义元素内的最后一个元素。在这里,你将指定 bean 的属性值设置为对容器管理的另一个 bean(协作者)的引用。 The referenced bean is a dependency of the bean whose property is to be set, and it is initialized on demand as needed before the property is set. 引用的 bean 是要设置属性的 bean 的依赖项,且在设置属性之前按需初始化。 (If the collaborator is a singleton bean, it may already be initialized by the container.) All references are ultimately a reference to another object. Scoping and validation depend on whether you specify the ID or name of the other object through the bean or parent attribute. (如果协作者是单例 bean,它可能已经被容器初始化。)所有引用最终都是对另一个对象的引用。作用域和验证取决于你是否通过 beanparent 属性指定了另一个对象的 ID 或名称。

Specifying the target bean through the bean attribute of the <ref/> tag is the most general form and allows creation of a reference to any bean in the same container or parent container, regardless of whether it is in the same XML file. The value of the bean attribute may be the same as the id attribute of the target bean or be the same as one of the values in the name attribute of the target bean. The following example shows how to use a ref element: 通过 <ref/> 标签的 bean 属性指定目标 bean 是最通用的形式,允许创建对同一容器或父容器中任何 bean 的引用,无论它是否在同一 XML 文件中。 bean 属性的值可能与目标 bean 的 id 属性相同,或者与目标 bean 的 name 属性中的某个值相同。以下示例展示了如何使用 ref 元素:

Specifying the target bean through the parent attribute creates a reference to a bean that is in a parent container of the current container. The value of the parent attribute may be the same as either the id attribute of the target bean or one of the values in the name attribute of the target bean. The target bean must be in a parent container of the current one. 通过 parent 属性指定目标 bean,在当前容器父容器中创建对该 bean 的引用。 parent 属性的值可以是目标 bean 的 id 属性或目标 bean 的 name 属性中的任何一个值。目标 bean 必须位于当前容器的父容器中。 You should use this bean reference variant mainly when you have a hierarchy of containers and you want to wrap an existing bean in a parent container with a proxy that has the same name as the parent bean. The following pair of listings shows how to use the parent attribute: 你应该主要在具有容器层次结构且你想使用与父 bean 同名代理将现有 bean 包装在父容器中时使用此 bean 引用变体。以下对列表展示了如何使用 parent 属性:

<!-- in the parent context --> <bean id="accountService" class="com.something.SimpleAccountService"> <!-- insert dependencies as required here --> </bean>
<!-- in the child (descendant) context --> <bean id="accountService" <!-- bean name is the same as the parent bean --> class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <ref parent="accountService"/> <!-- notice how we refer to the parent bean --> </property> <!-- insert other configuration and dependencies as required here --> </bean>

The local attribute on the ref element is no longer supported in the 4.0 beans XSD, since it does not provide value over a regular bean reference any more. Change your existing ref local references to ref bean when upgrading to the 4.0 schema. local 属性在 4.0 版本的 beans XSD 中不再受支持,因为它不再比常规的 bean 引用提供任何价值。在升级到 4.0 架构时,请将现有的 ref local 引用更改为 ref bean

Inner Beans 内部 Bean

A <bean/> element inside the <property/> or <constructor-arg/> elements defines an inner bean, as the following example shows: 一个 <bean/> 元素在 <property/><constructor-arg/> 元素内部定义了一个内部 bean,如下例所示:


<bean id="outer" class="...">
    <!-- instead of using a reference to a target bean, simply define the target bean inline -->
    <property name="target">
        <bean class="com.example.Person"> <!-- this is the inner bean -->
            <property name="name" value="Fiona Apple"/>
            <property name="age" value="25"/>
        </bean>
    </property>
</bean>

An inner bean definition does not require a defined ID or name. If specified, the container does not use such a value as an identifier. The container also ignores the scope flag on creation, because inner beans are always anonymous and are always created with the outer bean. It is not possible to access inner beans independently or to inject them into collaborating beans other than into the enclosing bean. 内部 Bean 定义不需要定义 ID 或名称。如果指定了,容器不会将此值用作标识符。容器在创建时也会忽略 scope 标志,因为内部 Bean 始终是匿名的,并且总是与外部 Bean 一起创建。无法独立访问内部 Bean,也无法将其注入到封装 Bean 之外的协作 Bean 中。

As a corner case, it is possible to receive destruction callbacks from a custom scope — for example, for a request-scoped inner bean contained within a singleton bean. 作为一个边缘情况,可能从自定义作用域接收到销毁回调——例如,对于包含在单例 bean 中的请求作用域内部 bean。 The creation of the inner bean instance is tied to its containing bean, but destruction callbacks let it participate in the request scope’s lifecycle. This is not a common scenario. Inner beans typically simply share their containing bean’s scope. 内部 Bean 实例的创建与其包含的 Bean 相关联,但销毁回调允许它参与请求作用域的生命周期。这种情况并不常见。内部 Bean 通常只是共享其包含 Bean 的作用域。

(#beans-collection-elements)Collections 收藏

The <list/>, <set/>, <map/>, and <props/> elements set the properties and arguments of the Java Collection types List, Set, Map, and Properties, respectively. The following example shows how to use them: <list/><set/><map/><props/> 元素分别设置 Java Collection 类型 ListSetMapProperties 的属性和参数。以下示例展示了如何使用它们:

<bean id="moreComplexObject" class="example.ComplexObject"> <!-- results in a setAdminEmails(java.util.Properties) call --> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.org</prop> <prop key="support">support@example.org</prop> <prop key="development">development@example.org</prop> </props> </property> <!-- results in a setSomeList(java.util.List) call --> <property name="someList"> <list> <value>a list element followed by a reference</value> <ref bean="myDataSource"/> </list> </property> <!-- results in a setSomeMap(java.util.Map) call --> <property name="someMap"> <map> <entry key="an entry" value="just some string"/> <entry key="a ref" value-ref="myDataSource"/> </map> </property> <!-- results in a setSomeSet(java.util.Set) call --> <property name="someSet"> <set> <value>just some string</value> <ref bean="myDataSource"/> </set> </property> </bean>

The value of a map key or value, or a set value, can also be any of the following elements: 地图键或值的值,或集合值的值也可以是以下任何元素:

bean | ref | idref | list | set | map | props | value | null

(#beans-collection-elements-merging)Collection Merging 集合合并

The Spring container also supports merging collections. An application developer can define a parent <list/>, <map/>, <set/> or <props/> element and have child <list/>, <map/>, <set/> or <props/> elements inherit and override values from the parent collection. That is, the child collection’s values are the result of merging the elements of the parent and child collections, with the child’s collection elements overriding values specified in the parent collection. Spring 容器也支持合并集合。应用程序开发者可以定义一个父 <list/><map/><set/><props/> 元素,并让子 <list/><map/><set/><props/> 元素继承并覆盖父集合中的值。也就是说,子集合的值是合并父集合和子集合元素的结果,子集合的元素会覆盖父集合中指定的值。

This section on merging discusses the parent-child bean mechanism. Readers unfamiliar with parent and child bean definitions may wish to read the relevant section before continuing. 本节讨论了父子 Bean 机制。对父 Bean 和子 Bean 定义不熟悉的读者,在继续之前可能希望阅读相关部分。

The following example demonstrates collection merging: 以下示例演示了集合合并:

<beans> <bean id="parent" abstract="true" class="example.ComplexObject"> <property name="adminEmails"> <props> <prop key="administrator">administrator@example.com</prop> <prop key="support">support@example.com</prop> </props> </property> </bean> <bean id="child" parent="parent"> <property name="adminEmails"> <!-- the merge is specified on the child collection definition --> <props merge="true"> <prop key="sales">sales@example.com</prop> <prop key="support">support@example.co.uk</prop> </props> </property> </bean> <beans>

Notice the use of the merge=true attribute on the <props/> element of the adminEmails property of the child bean definition. When the child bean is resolved and instantiated by the container, the resulting instance has an adminEmails Properties collection that contains the result of merging the child’s adminEmails collection with the parent’s adminEmails collection. The following listing shows the result: 请注意在 child bean 定义的 adminEmails 属性的 <props/> 元素上使用 merge=true 属性。当容器解析并实例化 child bean 时,生成的实例具有一个 adminEmails Properties 集合,该集合包含将子集的 adminEmails 集合与父集的 adminEmails 集合合并的结果。以下列表显示了结果:

administrator=administrator@example.com)

sales=sales@example.com)

support=support@example.co.uk

The child Properties collection’s value set inherits all property elements from the parent <props/>, and the child’s value for the support value overrides the value in the parent collection. 子集 Properties 的值集继承自父集 <props/> 的所有属性元素,子集的 support 值覆盖了父集合中的值。

This merging behavior applies similarly to the <list/>, <map/>, and <set/> collection types. In the specific case of the <list/> element, the semantics associated with the List collection type (that is, the notion of an ordered collection of values) is maintained. The parent’s values precede all of the child list’s values. In the case of the Map, Set, and Properties collection types, no ordering exists. Hence, no ordering semantics are in effect for the collection types that underlie the associated Map, Set, and Properties implementation types that the container uses internally. 此合并行为同样适用于 <list/> <map/> <set/> 集合类型。在 <list/> 元素的具体情况下,与 List 集合类型相关的语义(即值集合的概念)得到保留。父元素的值先于子列表的所有值。在 MapSetProperties 集合类型的情况下,不存在排序。因此,对于容器内部使用的相关 MapSetProperties 实现类型所基于的集合类型,没有生效的排序语义。

(#beans-collection-merge-limitations)Limitations of Collection Merging

合并收集的限制

You cannot merge different collection types (such as a Map and a List). If you do attempt to do so, an appropriate Exception is thrown. The merge attribute must be specified on the lower, inherited, child definition. Specifying the merge attribute on a parent collection definition is redundant and does not result in the desired merging. 你不能合并不同的集合类型(例如 MapList )。如果你尝试这样做,将抛出适当的 Exception 。必须在较低、继承的子定义上指定 merge 属性。在父集合定义上指定 merge 属性是多余的,并不会导致预期的合并。

(#beans-collection-elements-strongly-typed)Strongly-typed collection

强类型集合

Thanks to Java’s support for generic types, you can use strongly typed collections. That is, it is possible to declare a Collection type such that it can only contain (for example) String elements. If you use Spring to dependency-inject a strongly-typed Collection into a bean, you can take advantage of Spring’s type-conversion support such that the elements of your strongly-typed Collection instances are converted to the appropriate type prior to being added to the Collection. The following Java class and bean definition show how to do so: 感谢 Java 对泛型类型的支持,你可以使用强类型集合。也就是说,你可以声明一个只能包含(例如)元素的类型 Collection 。如果你使用 Spring 将强类型的 Collection 依赖注入到 bean 中,你可以利用 Spring 的类型转换支持,在将元素添加到 Collection 之前,将强类型 Collection 实例的元素转换为适当的类型。以下 Java 类和 bean 定义展示了如何实现:

public class SomeClass { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) { this.accounts = accounts; } }
class SomeClass { lateinit var accounts: Map<String, Float> }
<beans> <bean id="something" class="x.y.SomeClass"> <property name="accounts"> <map> <entry key="one" value="9.99"/> <entry key="two" value="2.75"/> <entry key="six" value="3.99"/> </map> </property> </bean> </beans>

When the accounts property of the something bean is prepared for injection, the generics information about the element type of the strongly-typed Map<String, Float> is available by reflection. Thus, Spring’s type conversion infrastructure recognizes the various value elements as being of type Float, and the string values (9.99, 2.75, and 3.99) are converted into an actual Float type. 当 somethingaccounts 属性准备注入时,通过反射可以获取强类型 Map<String, Float> 的元素类型的相关泛型信息。因此,Spring 的类型转换基础设施识别各种值元素为类型 Float ,并将字符串值( 9.992.753.99 )转换为实际的 Float 类型。

(#beans-null-element)Null and Empty String Values

空和空字符串值

Spring treats empty arguments for properties and the like as empty Strings. The following XML-based configuration metadata snippet sets the email property to the empty String value (""). Spring 将属性和类似项的空参数视为空 Strings 。以下基于 XML 的配置元数据片段将 email 属性设置为空 String 值("")。

<bean class="ExampleBean"> <property name="email" value=""/> </bean>

The preceding example is equivalent to the following Java code: 前述示例等价于以下 Java 代码:

exampleBean.setEmail("");

The <null/> element handles null values. The following listing shows an example: <null/> 元素处理 null 值。以下列表显示了一个示例:

<bean class="ExampleBean"> <property name="email"> <null/> </property> </bean>

The preceding configuration is equivalent to the following Java code: 前述配置等价于以下 Java 代码:

exampleBean.setEmail(null);

(#beans-p-namespace)XML Shortcut with the p-namespace

XML 使用 p-namespace 的快捷方式

The p-namespace lets you use the bean element’s attributes (instead of nested <property/> elements) to describe your property values collaborating beans, or both. p-命名空间允许你使用 bean 元素的属性(而不是嵌套的 <property/> 元素)来描述你的属性值、协作的 bean,或两者兼而有之。

Spring supports extensible configuration formats with namespaces, which are based on an XML Schema definition. The beans configuration format discussed in this chapter is defined in an XML Schema document. However, the p-namespace is not defined in an XSD file and exists only in the core of Spring. Spring 支持具有命名空间的可扩展配置格式,这些格式基于 XML Schema 定义。本章讨论的 beans 配置格式定义在 XML Schema 文档中。然而,p-namespace 未定义在 XSD 文件中,仅存在于 Spring 的核心中。

The following example shows two XML snippets (the first uses standard XML format and the second uses the p-namespace) that resolve to the same result: 以下示例显示了两个 XML 片段(第一个使用标准 XML 格式,第二个使用 p-命名空间),它们解析为相同的结果:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="classic" class="com.example.ExampleBean"> <property name="email" value="someone@somewhere.com"/> </bean> <bean name="p-namespace" class="com.example.ExampleBean" p:email="someone@somewhere.com"/> </beans>

The example shows an attribute in the p-namespace called email in the bean definition. This tells Spring to include a property declaration. As previously mentioned, the p-namespace does not have a schema definition, so you can set the name of the attribute to the property name. 示例显示了在 bean 定义中 p-namespace 下的一个名为 email 的属性。这告诉 Spring 包含一个属性声明。如前所述,p-namespace 没有模式定义,因此你可以设置属性名称为属性名。

This next example includes two more bean definitions that both have a reference to another bean: 这个例子包括两个额外的 bean 定义,这两个定义都引用了另一个 bean:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="john-classic" class="com.example.Person"> <property name="name" value="John Doe"/> <property name="spouse" ref="jane"/> </bean> <bean name="john-modern" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/> <bean name="jane" class="com.example.Person"> <property name="name" value="Jane Doe"/> </bean> </beans>

This example includes not only a property value using the p-namespace but also uses a special format to declare property references. Whereas the first bean definition uses <property name="spouse" ref="jane"/> to create a reference from bean john to bean jane, the second bean definition uses p:spouse-ref="jane" as an attribute to do the exact same thing. In this case, spouse is the property name, whereas the -ref part indicates that this is not a straight value but rather a reference to another bean. 此示例不仅包括使用 p-命名空间的属性值,还使用特殊格式来声明属性引用。第一个 bean 定义使用 <property name="spouse" ref="jane"/> 从 bean john 创建对 bean jane 的引用,而第二个 bean 定义使用 p:spouse-ref="jane" 作为属性执行相同操作。在这种情况下, spouse 是属性名,而 -ref 部分表示这不是一个直接值,而是一个对另一个 bean 的引用。

The p-namespace is not as flexible as the standard XML format. For example, the format for declaring property references clashes with properties that end in Ref, whereas the standard XML format does not. We recommend that you choose your approach carefully and communicate this to your team members to avoid producing XML documents that use all three approaches at the same time. p-命名空间不如标准 XML 格式灵活。例如,声明属性引用的格式与以 Ref 结尾的属性冲突,而标准 XML 格式则没有这种情况。我们建议你仔细选择方法,并将此信息传达给团队成员,以避免生成同时使用三种方法的 XML 文档。

(#beans-c-namespace)XML Shortcut with the c-namespace

XML 命名空间 c 的快捷方式

Similar to the XML Shortcut with the p-namespace, the c-namespace, introduced in Spring 3.1, allows inlined attributes for configuring the constructor arguments rather then nested constructor-arg elements. 与具有 p-命名空间的 XML 快捷方式类似,Spring 3.1 中引入的 c-命名空间允许内联属性来配置构造函数参数,而不是嵌套的 constructor-arg 元素。

The following example uses the c: namespace to do the same thing as the from Constructor-based Dependency Injection: 以下示例使用 c: 命名空间来完成与基于构造函数的依赖注入相同的事情:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="beanTwo" class="x.y.ThingTwo"/> <bean id="beanThree" class="x.y.ThingThree"/> <!-- traditional declaration with optional argument names --> <bean id="beanOne" class="x.y.ThingOne"> <constructor-arg name="thingTwo" ref="beanTwo"/> <constructor-arg name="thingThree" ref="beanThree"/> <constructor-arg name="email" value="something@somewhere.com"/> </bean> <!-- c-namespace declaration with argument names --> <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo" c:thingThree-ref="beanThree" c:email="something@somewhere.com"/> </beans>

The c: namespace uses the same conventions as the p: one (a trailing -ref for bean references) for setting the constructor arguments by their names. Similarly, it needs to be declared in the XML file even though it is not defined in an XSD schema (it exists inside the Spring core).

For the rare cases where the constructor argument names are not available (usually if the bytecode was compiled without debugging information), you can use fallback to the argument indexes, as follows:

<!-- c-namespace index declaration --> <bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree" c:_2="something@somewhere.com"/>

Due to the XML grammar, the index notation requires the presence of the leading _, as XML attribute names cannot start with a number (even though some IDEs allow it). A corresponding index notation is also available for <constructor-arg> elements but not commonly used since the plain order of declaration is usually sufficient there.

In practice, the constructor resolution mechanism is quite efficient in matching arguments, so unless you really need to, we recommend using the name notation throughout your configuration.

(#beans-compound-property-names)Compound Property Names

You can use compound or nested property names when you set bean properties, as long as all components of the path except the final property name are not null. Consider the following bean definition:

<bean id="something" class="things.ThingOne"> <property name="fred.bob.sammy" value="123"/> </bean>

The something bean has a fred property, which has a bob property, which has a sammy property, and that final sammy property is being set to a value of 123. In order for this to work, the fred property of something and the bob property of fred must not be null after the bean is constructed. Otherwise, a NullPointerException is thrown.

(#beans-factory-dependson)1.4.3. Usingdepends-on

If a bean is a dependency of another bean, that usually means that one bean is set as a property of another. Typically you accomplish this with the <ref/> element in XML-based configuration metadata. However, sometimes dependencies between beans are less direct. An example is when a static initializer in a class needs to be triggered, such as for database driver registration. The depends-on attribute can explicitly force one or more beans to be initialized before the bean using this element is initialized. The following example uses the depends-on attribute to express a dependency on a single bean:

<bean id="beanOne" class="ExampleBean" depends-on="manager"/> <bean id="manager" class="ManagerBean"/>

To express a dependency on multiple beans, supply a list of bean names as the value of the depends-on attribute ( commas, whitespace, and semicolons are valid delimiters):

<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao"> <property name="manager" ref="manager"/> </bean> <bean id="manager" class="ManagerBean"/> <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao"/>

The depends-on attribute can specify both an initialization-time dependency and, in the case of singleton beans only, a corresponding destruction-time dependency. Dependent beans that define a depends-on relationship with a given bean are destroyed first, prior to the given bean itself being destroyed. Thus, depends-on can also control shutdown order.

1.4.4. Lazy-initialized Beans

By default, ApplicationContext implementations eagerly create and configure all singleton beans as part of the initialization process. Generally, this pre-instantiation is desirable, because errors in the configuration or surrounding environment are discovered immediately, as opposed to hours or even days later. When this behavior is not desirable, you can prevent pre-instantiation of a singleton bean by marking the bean definition as being lazy-initialized. A lazy-initialized bean tells the IoC container to create a bean instance when it is first requested, rather than at startup.

In XML, this behavior is controlled by the lazy-init attribute on the <bean/> element, as the following example shows:

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/> <bean name="not.lazy" class="com.something.AnotherBean"/>

When the preceding configuration is consumed by an ApplicationContext, the lazy bean is not eagerly pre-instantiated when the ApplicationContext starts, whereas the not.lazy bean is eagerly pre-instantiated.

However, when a lazy-initialized bean is a dependency of a singleton bean that is not lazy-initialized, the ApplicationContext creates the lazy-initialized bean at startup, because it must satisfy the singleton’s dependencies. The lazy-initialized bean is injected into a singleton bean elsewhere that is not lazy-initialized.

You can also control lazy-initialization at the container level by using the default-lazy-init attribute on the <beans/> element, as the following example shows:

<beans default-lazy-init="true"> <!-- no beans will be pre-instantiated... --> </beans>

1.4.5. Autowiring Collaborators

The Spring container can autowire relationships between collaborating beans. You can let Spring resolve collaborators ( other beans) automatically for your bean by inspecting the contents of the ApplicationContext. Autowiring has the following advantages: Spring 容器可以自动连接协作 Bean 之间的关系。你可以通过检查 ApplicationContext 的内容,让 Spring 自动为你 Bean 解决协作者(其他 Bean)。自动装配有以下优点:

  • Autowiring can significantly reduce the need to specify properties or constructor arguments. (Other mechanisms such as a bean template discussed elsewhere in this chapter are also valuable in this regard.) 自动装配可以显著减少指定属性或构造函数参数的需求。(本章其他地方讨论的如 bean 模板等机制在这方面也非常有价值。)

  • Autowiring can update a configuration as your objects evolve. For example, if you need to add a dependency to a class, that dependency can be satisfied automatically without you needing to modify the configuration. 自动装配可以根据对象的演变更新配置。例如,如果你需要向一个类添加依赖项,这个依赖项可以自动满足,而无需你修改配置。 Thus autowiring can be especially useful during development, without negating the option of switching to explicit wiring when the code base becomes more stable. 因此,自动装配在开发期间特别有用,而不会否定在代码库变得更加稳定时切换到显式装配的选项。

When using XML-based configuration metadata (see Dependency Injection), you can specify the autowire mode for a bean definition with the autowire attribute of the <bean/> element. The autowiring functionality has four modes. You specify autowiring per bean and can thus choose which ones to autowire. The following table describes the four autowiring modes: 当使用基于 XML 的配置元数据(见依赖注入)时,你可以使用 <bean/> 元素的 autowire 属性指定 bean 定义的自动装配模式。自动装配功能有四种模式。你可以按 bean 指定自动装配,从而选择要自动装配的 bean。以下表格描述了四种自动装配模式:

Table 2. Autowiring modes 表 2. 自动装配模式

Mode 模式

Explanation 说明

no

(Default) No autowiring. Bean references must be defined by ref elements. Changing the default setting is not recommended for larger deployments, because specifying collaborators explicitly gives greater control and clarity. To some extent, it documents the structure of a system. (默认) 不自动装配。必须通过 ref 元素定义 Bean 引用。对于较大的部署,不建议更改默认设置,因为明确指定协作者可以提供更大的控制和清晰度。在某种程度上,它记录了系统的结构。

byName

Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master and uses it to set the property. 通过属性名称自动装配。Spring 会寻找与需要自动装配的属性同名的一个 bean。例如,如果 bean 定义设置为按名称自动装配,并且它包含一个 master 属性(即它有一个 setMaster(..) 方法),Spring 会寻找一个名为 master 的 bean 定义并使用它来设置该属性。

byType

Lets a property be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens (the property is not set). 让属性自动装配,如果容器中恰好存在一个属性类型的 bean。如果存在多个,将抛出致命异常,这表明你可能无法使用 byType 自动装配该 bean。如果没有匹配的 bean,则不执行任何操作(属性未设置)。

constructor

Analogous to byType but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised. 类似于 byType ,但应用于构造函数参数。如果容器中不存在恰好一个构造函数参数类型的 bean,则会引发致命错误。

With byType or constructor autowiring mode, you can wire arrays and typed collections. In such cases, all autowire candidates within the container that match the expected type are provided to satisfy the dependency. You can autowire strongly-typed Map instances if the expected key type is String. An autowired Map instance’s values consist of all bean instances that match the expected type, and the Map instance’s keys contain the corresponding bean names. 使用 byTypeconstructor 自动装配模式,你可以连接数组和类型化集合。在这种情况下,容器内所有与预期类型匹配的自动装配候选者都会被提供以满足依赖。如果预期键类型是 String ,你可以自动装配强类型化的 Map 实例。自动装配的 Map 实例的值由所有匹配预期类型的 bean 实例组成,而 Map 实例的键包含相应的 bean 名称。

(#beans-autowired-exceptions)Limitations and Disadvantages of Autowiring

自动装配的局限性和缺点

Autowiring works best when it is used consistently across a project. If autowiring is not used in general, it might be confusing to developers to use it to wire only one or two bean definitions. 自动装配在项目内一致使用时效果最佳。如果通常不使用自动装配,开发者可能会对仅使用它来连接一个或两个 bean 定义感到困惑。

Consider the limitations and disadvantages of autowiring: 考虑自动装配的局限性和缺点:

  • Explicit dependencies in property and constructor-arg settings always override autowiring. You cannot autowire simple properties such as primitives, Strings, and Classes (and arrays of such simple properties). This limitation is by-design. 显式依赖在 propertyconstructor-arg 设置中始终覆盖自动装配。你不能自动装配简单属性,如原始类型、 StringsClasses (以及此类简单属性的数组)。这种限制是设计上的。

  • Autowiring is less exact than explicit wiring. Although, as noted in the earlier table, Spring is careful to avoid guessing in case of ambiguity that might have unexpected results. The relationships between your Spring-managed objects are no longer documented explicitly. 自动装配不如显式装配精确。尽管如此,正如前面表格中提到的,Spring 会小心避免在可能产生意外结果的歧义情况下进行猜测。你 Spring 管理的对象之间的关系不再被明确记录。

  • Wiring information may not be available to tools that may generate documentation from a Spring container. 接线信息可能无法提供给可能从 Spring 容器生成文档的工具。

  • Multiple bean definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or Map instances, this is not necessarily a problem. However, for dependencies that expect a single value, this ambiguity is not arbitrarily resolved. If no unique bean definition is available, an exception is thrown. 容器中可能存在多个与 setter 方法或构造函数参数指定的类型匹配的 bean 定义,以实现自动装配。对于数组、集合或 Map 实例,这通常不是问题。然而,对于期望单个值的依赖项,这种歧义不能随意解决。如果没有可用的唯一 bean 定义,将抛出异常。

In the latter scenario, you have several options: 在后者情况下,你有几个选择:

  • Abandon autowiring in favor of explicit wiring. 放弃自动布线,改为显式布线。

  • Avoid autowiring for a bean definition by setting its autowire-candidate attributes to false, as described in the next section. 避免通过设置其 autowire-candidate 属性为 false 来自动装配 bean 定义,如下一节所述。

  • Designate a single bean definition as the primary candidate by setting the primary attribute of its <bean/> element to true. 指定单个 bean 定义为主要候选者,通过将其 <bean/> 元素的 primary 属性设置为 true

  • Implement the more fine-grained control available with annotation-based configuration, as described in Annotation-based Container Configuration. 实现如《基于注解的容器配置》中所述的基于注解的配置提供的更细粒度控制。

(#beans-factory-autowire-candidate)Excluding a Bean from Autowiring

排除 Bean 从自动装配中

On a per-bean basis, you can exclude a bean from autowiring. In Spring’s XML format, set the autowire-candidate attribute of the <bean/> element to false. The container makes that specific bean definition unavailable to the autowiring infrastructure (including annotation style configurations such as @Autowired). 按豆子逐个排除,你可以禁用自动装配的豆子。在 Spring 的 XML 格式中,将 <bean/> 元素的 autowire-candidate 属性设置为 false 。容器使该特定豆子定义对自动装配基础设施不可用(包括 @Autowired 等注解风格配置)。

The autowire-candidate attribute is designed to only affect type-based autowiring. It does not affect explicit references by name, which get resolved even if the specified bean is not marked as an autowire candidate. As a consequence, autowiring by name nevertheless injects a bean if the name matches. autowire-candidate 属性仅设计用于影响基于类型的自动装配。它不影响通过名称显式引用,即使指定的 bean 未标记为自动装配候选者,这些引用也会得到解决。因此,通过名称的自动装配仍然会在名称匹配时注入一个 bean。

You can also limit autowire candidates based on pattern-matching against bean names. The top-level <beans/> element accepts one or more patterns within its default-autowire-candidates attribute. For example, to limit autowire candidate status to any bean whose name ends with Repository, provide a value of *Repository. To provide multiple patterns, define them in a comma-separated list. An explicit value of true or false for a bean definition’s autowire-candidate attribute always takes precedence. For such beans, the pattern matching rules do not apply. 你还可以根据与 bean 名称的模式匹配来限制自动装配的候选者。顶级 <beans/> 元素在其 default-autowire-candidates 属性中接受一个或多个模式。例如,要将自动装配候选者状态限制为任何以 Repository 结尾的 bean,请提供 *Repository 的值。要提供多个模式,请以逗号分隔的列表定义它们。对于 bean 定义的 autowire-candidate 属性,显式值 truefalse 始终具有优先权。对于此类 bean,不适用模式匹配规则。

These techniques are useful for beans that you never want to be injected into other beans by autowiring. It does not mean that an excluded bean cannot itself be configured by using autowiring. Rather, the bean itself is not a candidate for autowiring other beans. 这些技术适用于你永远不会希望被自动装配注入到其他豆类中的豆类。这并不意味着被排除的豆类不能通过自动装配进行配置。相反,这个豆类本身不是自动装配其他豆类的候选者。

(#beans-factory-method-injection)1.4.6. Method Injection 1.4.6. 方法注入

In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. 在大多数应用场景中,容器中的大多数豆类都是单例。当一个单例豆需要与其他单例豆协作,或者一个非单例豆需要与其他非单例豆协作时,你通常通过将一个豆定义为另一个豆的属性来处理这种依赖关系。 A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. 当 Bean 的生命周期不同时会出现问题。假设单例 Bean A 需要使用非单例(原型)Bean B,可能在 A 的每次方法调用时。 The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed. 容器只创建单例 bean A 一次,因此只有一次机会设置属性。容器不能每次需要时都为 bean A 提供 bean B 的新实例。

A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware interface, and by making a getBean("B") call to the container ask for (a typically new) bean B instance every time bean A needs it. The following example shows this approach: 一种解决方案是放弃一些控制反转。你可以通过实现 ApplicationContextAware 接口,并通过对容器进行 getBean("B") 调用来让 bean A 意识到容器,每次 bean A 需要时都请求(通常是新的)bean B 实例。以下示例展示了这种方法:

// a class that uses a stateful Command-style class to perform some processing package fiona.apple; // Spring-API imports import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; public class CommandManager implements ApplicationContextAware { private ApplicationContext applicationContext; public Object process(Map commandState) { // grab a new instance of the appropriate Command Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } protected Command createCommand() { // notice the Spring API dependency! return this.applicationContext.getBean("command", Command.class); } public void setApplicationContext( ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
// a class that uses a stateful Command-style class to perform some processing package fiona.apple // Spring-API imports import org.springframework.context.ApplicationContext import org.springframework.context.ApplicationContextAware class CommandManager : ApplicationContextAware { private lateinit var applicationContext: ApplicationContext fun process(commandState: Map<*, *>): Any { // grab a new instance of the appropriate Command val command = createCommand() // set the state on the (hopefully brand new) Command instance command.state = commandState return command.execute() } // notice the Spring API dependency! protected fun createCommand() = applicationContext.getBean("command", Command::class.java) override fun setApplicationContext(applicationContext: ApplicationContext) { this.applicationContext = applicationContext } }

The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, lets you handle this use case cleanly. 前述内容不理想,因为业务代码已了解并耦合到 Spring 框架中。方法注入是 Spring IoC 容器的一个相对高级特性,允许你干净地处理此用例。

(#beans-factory-lookup-method-injection)Lookup Method Injection 查找方法注入

Lookup method injection is the ability of the container to override methods on container-managed beans and return the lookup result for another named bean in the container. The lookup typically involves a prototype bean, as in the scenario described in the preceding section. The Spring Framework implements this method injection by using bytecode generation from the CGLIB library to dynamically generate a subclass that overrides the method. 查找方法注入是容器覆盖容器管理的 bean 上方法并返回容器中另一个命名 bean 的查找结果的能力。查找通常涉及原型 bean,如前一小节所述的场景。Spring 框架通过使用 CGLIB 库的字节码生成来动态生成一个覆盖方法的子类来实现此方法注入。

  • For this dynamic subclassing to work, the class that the Spring bean container subclasses cannot be final, and the method to be overridden cannot be final, either. 为此动态子类化要生效,Spring bean 容器所子类的类不能是 final ,要重写的方法也不能是 final

  • Unit-testing a class that has an abstract method requires you to subclass the class yourself and to supply a stub implementation of the abstract method. 单元测试具有 abstract 方法的类需要你自己对该类进行子类化,并提供一个 abstract 方法的存根实现。

  • Concrete methods are also necessary for component scanning, which requires concrete classes to pick up. 混凝土方法对于组件扫描也是必要的,这需要具体的类来拾取。

  • A further key limitation is that lookup methods do not work with factory methods and in particular not with @Bean methods in configuration classes, since, in that case, the container is not in charge of creating the instance and therefore cannot create a runtime-generated subclass on the fly. 进一步的关键限制是,查找方法不适用于工厂方法,尤其是配置类中的 @Bean 方法,因为在这种情况下,容器不负责创建实例,因此无法动态创建运行时生成的子类。

In the case of the CommandManager class in the previous code snippet, the Spring container dynamically overrides the implementation of the createCommand() method. The CommandManager class does not have any Spring dependencies, as the reworked example shows: 在上一段代码片段中的 CommandManager 类中,Spring 容器动态覆盖了 createCommand() 方法的实现。 CommandManager 类没有任何 Spring 依赖,如重构后的示例所示:

package fiona.apple; // no more Spring imports! public abstract class CommandManager { public Object process(Object commandState) { // grab a new instance of the appropriate Command interface Command command = createCommand(); // set the state on the (hopefully brand new) Command instance command.setState(commandState); return command.execute(); } // okay... but where is the implementation of this method? protected abstract Command createCommand(); }
package fiona.apple // no more Spring imports! abstract class CommandManager { fun process(commandState: Any): Any { // grab a new instance of the appropriate Command interface val command = createCommand() // set the state on the (hopefully brand new) Command instance command.state = commandState return command.execute() } // okay... but where is the implementation of this method? protected abstract fun createCommand(): Command }

In the client class that contains the method to be injected (the CommandManager in this case), the method to be injected requires a signature of the following form:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

If the method is abstract, the dynamically-generated subclass implements the method. Otherwise, the dynamically-generated subclass overrides the concrete method defined in the original class. Consider the following example: 如果方法是 abstract ,则动态生成的子类实现了该方法。否则,动态生成的子类覆盖了原始类中定义的具体方法。考虑以下示例:

<!-- a stateful bean deployed as a prototype (non-singleton) --> <bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype"> <!-- inject dependencies here as required --> </bean> <!-- commandProcessor uses statefulCommandHelper --> <bean id="commandManager" class="fiona.apple.CommandManager"> <lookup-method name="createCommand" bean="myCommand"/> </bean>

The bean identified as commandManager calls its own createCommand() method whenever it needs a new instance of the myCommand bean. You must be careful to deploy the myCommand bean as a prototype if that is actually what is needed. If it is a singleton, the same instance of the myCommand bean is returned each time. 该被标识为 commandManager 的 bean 在需要一个新的 myCommand bean 实例时,会调用其自身的 createCommand() 方法。如果实际上需要将 myCommand bean 作为原型部署,你必须小心处理。如果是单例,每次都会返回相同的 myCommand bean 实例。

Alternatively, within the annotation-based component model, you can declare a lookup method through the @Lookup annotation, as the following example shows: 或者,在基于注解的组件模型中,你可以通过 @Lookup 注解声明一个查找方法,如下例所示:

public abstract class CommandManager { public Object process(Object commandState) { Command command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup("myCommand") protected abstract Command createCommand(); }
abstract class CommandManager { fun process(commandState: Any): Any { val command = createCommand() command.state = commandState return command.execute() } @Lookup("myCommand") protected abstract fun createCommand(): Command }

Or, more idiomatically, you can rely on the target bean getting resolved against the declared return type of the lookup method: 或者,更通俗地说,你可以依赖目标 bean 根据查找方法的声明返回类型来解析:

public abstract class CommandManager { public Object process(Object commandState) { Command command = createCommand(); command.setState(commandState); return command.execute(); } @Lookup protected abstract Command createCommand(); }
abstract class CommandManager { fun process(commandState: Any): Any { val command = createCommand() command.state = commandState return command.execute() } @Lookup protected abstract fun createCommand(): Command }

Note that you should typically declare such annotated lookup methods with a concrete stub implementation, in order for them to be compatible with Spring’s component scanning rules where abstract classes get ignored by default. 请注意,通常你应该使用具体的存根实现来声明这样的注解查找方法,以便它们与 Spring 的组件扫描规则兼容,其中默认情况下会忽略抽象类。 This limitation does not apply to explicitly registered or explicitly imported bean classes. 此限制不适用于显式注册或显式导入的 Bean 类。

Another way of accessing differently scoped target beans is an ObjectFactory/ Provider injection point. See Scoped Beans as Dependencies. 另一种访问不同作用域的目标 bean 的方法是使用 ObjectFactory / Provider 注入点。参见“将作用域 bean 作为依赖项”。

You may also find the ServiceLocatorFactoryBean (in the org.springframework.beans.factory.config package) to be useful. 你也可能发现 ServiceLocatorFactoryBean (在 org.springframework.beans.factory.config 包中)很有用。

(#beans-factory-arbitrary-method-replacement)Arbitrary Method Replacement

任意方法替换

A less useful form of method injection than lookup method injection is the ability to replace arbitrary methods in a managed bean with another method implementation. You can safely skip the rest of this section until you actually need this functionality. 不如查找方法注入有用的一种方法注入形式是能够在托管 Bean 中用另一个方法实现替换任意方法。你可以在真正需要此功能之前安全地跳过本节的其余部分。

With XML-based configuration metadata, you can use the replaced-method element to replace an existing method implementation with another, for a deployed bean. Consider the following class, which has a method called computeValue that we want to override: 基于 XML 配置元数据,你可以使用 replaced-method 元素用另一个替换现有方法实现,用于已部署的 bean。考虑以下类,其中有一个我们想要覆盖的方法 computeValue

public class MyValueCalculator { public String computeValue(String input) { // some real code... } // some other methods... }
class MyValueCalculator { fun computeValue(input: String): String { // some real code... } // some other methods... }

A class that implements the org.springframework.beans.factory.support.MethodReplacer interface provides the new method definition, as the following example shows: 一个实现了 org.springframework.beans.factory.support.MethodReplacer 接口的类提供了新的方法定义,如下例所示:

/** * meant to be used to override the existing computeValue(String) * implementation in MyValueCalculator */ public class ReplacementComputeValue implements MethodReplacer { public Object reimplement(Object o, Method m, Object args) throws Throwable { // get the input value, work with it, and return a computed result String input = (String) args[0]; ... return ...; } }
/** * meant to be used to override the existing computeValue(String) * implementation in MyValueCalculator */ class ReplacementComputeValue : MethodReplacer { override fun reimplement(obj: Any, method: Method, args: Array<out Any>): Any { // get the input value, work with it, and return a computed result val input = args[0] as String; ... return ...; } }

The bean definition to deploy the original class and specify the method override would resemble the following example: 部署原始类并指定方法覆盖的 Bean 定义将类似于以下示例:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator"> <!-- arbitrary method replacement --> <replaced-method name="computeValue" replacer="replacementComputeValue"> <arg-type>String</arg-type> </replaced-method> </bean> <bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

You can use one or more <arg-type/> elements within the <replaced-method/> element to indicate the method signature of the method being overridden. The signature for the arguments is necessary only if the method is overloaded and multiple variants exist within the class. 你可以在 <replaced-method/> 元素中使用一个或多个 <arg-type/> 元素来指示被重写方法的签名。如果方法被重载且在类中存在多个变体,则必须指定参数的签名。 For convenience, the type string for an argument may be a substring of the fully qualified type name. For example, the following all match java.lang.String: 为了方便,参数的类型字符串可以是完全限定类型名称的子串。例如,以下所有匹配 java.lang.String :

java.lang.String String Str

Because the number of arguments is often enough to distinguish between each possible choice, this shortcut can save a lot of typing, by letting you type only the shortest string that matches an argument type. 因为参数的数量通常足以区分每个可能的选择,所以这个快捷方式可以通过只输入与参数类型匹配的最短字符串来节省大量的输入。