Spring Core 之 Dependency Injection(依赖注入)

生词汇总

  • coherent ----------------------- 相干, 连贯, 通
  • mock ---------------------------- 嘲笑, 嘲弄, 模仿
  • conjunction --------------------- 连词, 连接词, 关联
  • programmatically --------------- 以编程方式
  • internally into -------------------- 内部进入
  • immutable ------------------------- 不可变的
  • refactored --------------------------- 重构
  • performed -------------------------- 执行
  • amenable ------------------------- 服从的, 肯服从的
  • predominantly -------------------- 主要是
  • alternatively ----------------------- 或者
  • straight --------------------------- 直, 直接, 直行, 海峡
  • succint ---------------------------- 简洁的
  • snippet ------------------------------ 片段
  • hierarchy --------------------------- 阶层, 等级制度
  • semicolons ------------------------- 分号
  • desirable ---------------------------- 可取的
  • especially ------------------------- 特别, 尤其
  • negate ------------------------------- 否定
  • Analogous -------------------------- 类似的, 相似的
  • On sth basis ----------------------- 以…为基础
  • precedence ------------------------ 优先权, 位次
  • arbitrarily ------------------------------ 任意地, 武断地
  • arises ---------------------------------- 出现
  • forego --------------------------------- 放弃, 在 … 前
  • coupled ------------------------------- 耦合
  • somewhat ------------------------------ 有些, 稍微
  • concrete ------------------------------- 具体的
  • idiomatically -------------------------- 惯用的

一、Dependencies Overview(依赖概述)

A typical enterprise application does not consist of a single object.Even the simplest application has a few objects that work together to present what the end-user sees as a coherent application.Dependency injection is a process whereby objects define their dependencies only through constructor arguments,arguments to a factory method,or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes or the Service Locator pattern.

一个常规的企业应用程序不会只由单个对象构成,即使是最简单的应用程序也有一些对象,它们协同工作以呈现最终用户所看到的连贯应用程序。依赖注入 (DI) 是一个过程,其中对象仅通过构造函数参数定义它们的依赖项(即,它们使用的其他对象),工厂方法的参数,或在对象实例被构造或从工厂方法返回后在对象实例上设置的属性。然后容器在创建 bean 时注入这些依赖项。这个过程基本上是 bean 本身的逆过程(因此得名,控制反转),通过使用类的直接构造或服务定位器模式自行控制其依赖项的实例化或位置。

Code is cleaner with the DI principle, and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies and does not know the location or class of the dependencies. As a result, your classes become easier to test, particularly when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.

DI 原则使代码更清晰,当对象提供其依赖关系时,解耦更有效。该对象不查找其依赖项,也不知道依赖项的位置或类。因此,您的类变得更容易测试,特别是当依赖项位于接口或抽象基类上时,这允许在单元测试中使用存根或模拟实现。

1、Constructor-based Dependency Injection(基于构造器注入)

Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency. Calling a static factory method with specific arguments to construct the bean is nearly equivalent, and this discussion treats arguments to a constructor and to a static factory method similarly. The following example shows a class that can only be dependency-injected with constructor injection:

基于构造函数的 DI 是通过容器调用具有多个参数的构造函数来完成的,每个参数代表一个依赖项。调用带有特定参数的静态工厂方法来构造 bean 几乎是等效的,并且这个讨论类似地对待构造函数和静态工厂方法的参数。以下示例显示了一个只能使用构造函数注入进行依赖注入的类:

public class SimpleMovieLister{
 // the SimpleMovieLister has a dependency on a MovieFinder
    private final MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

Constructor Argument Resolution 构造函数参数解析

Constructor argument resolution matching occurs by using the argument’s type. If no potential ambiguity exists in the constructor arguments of a bean definition, the order in which the constructor arguments are defined in a bean definition is the order in which those arguments are supplied to the appropriate constructor when the bean is being instantiated. Consider the following class:

构造函数参数解析匹配通过使用参数的类型发生。如果 bean 定义的构造函数参数中不存在潜在的歧义,在 bean 定义中定义构造函数参数的顺序是在实例化 bean 时将这些参数提供给适当的构造函数的顺序。考虑以下类:

public class ThingOne {

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

Assuming that the ThingTwo and ThingThree classes are not related by inheritance, no potential ambiguity exists. Thus, the following configuration works fine, and you do not need to specify the constructor argument indexes or types explicitly in the element.

(简体)
假设 ThingTwo 和 ThingThree 类没有通过继承关联,则不存在潜在的歧义。因此,以下配置工作正常,您不需要在 元素中显式指定构造函数参数索引或类型。

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

When another bean is referenced, the type is known, and matching can occur (as was the case with the preceding example). When a simple type is used, such as true, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class:

当另一个 bean 被引用时,类型是已知的,并且可以发生匹配(就像前面的例子一样)。当使用简单类型时,例如 true,Spring 无法确定值的类型,因此无法在没有帮助的情况下按类型进行匹配。考虑以下类:

public class ExampleBean {

    // Number of years to calculate the Ultimate Answer
    private final int years;

    // The Answer to Life, the Universe, and Everything
    private final String ultimateAnswer;

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

Constructor argument type matching

In the preceding scenario, the container can use type matching with simple types if you explicitly specify the type of the constructor argument by using the type attribute, as the following example shows:

在之前的场景中,如果您使用 type 属性显式指定构造函数参数的类型,则容器可以使用与简单类型的类型匹配,如以下示例所示:

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

Constructor argument index
You can use the index attribute to specify explicitly the index of constructor arguments, as the following example shows:
您可以使用 index 属性明确指定构造函数参数的索引,如以下示例所示:

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

Constructor argument name
You can also use the constructor parameter name for value disambiguation, as the following example shows:
您还可以使用构造函数参数名称进行值消歧,如以下示例所示:

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

@ConstructorProperties
You can use the @ConstructorProperties JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:
您可以使用 @ConstructorProperties JDK 注释显式命名构造函数参数。示例类必须如下所示:


public class ExampleBean {

    // Fields omitted

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

2、Setter-based Dependency Injection(基于Setter方法注入)

Setter-based DI is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or a no-argument static factory method to instantiate your bean.

The following example shows a class that can only be dependency-injected by using pure setter injection. This class is conventional Java. It is a POJO that has no dependencies on container specific interfaces, base classes, or annotations.

基于 Setter 的 DI 是通过容器在调用无参数构造函数或无参数静态工厂方法来实例化 bean 后调用 bean 上的 setter 方法来完成的。
以下示例显示了一个只能使用纯 setter 注入进行依赖注入的类。这个类是传统的Java。这个类是传统的Java。它是一个不依赖于容器特定接口、基类或注解的 POJO。

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

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.

ApplicationContext 为其管理的 bean 支持基于构造函数和基于 setter 的 DI。在已经通过构造函数方法注入了一些依赖项之后,它还支持基于 setter 的 DI。您以 BeanDefinition 的形式配置依赖项,您可以将其与 PropertyEditor 实例结合使用以将属性从一种格式转换为另一种格式。然而,大多数 Spring 用户并不直接(即以编程方式)使用这些类,而是使用 XML bean 定义、带注释的组件(即用 @Component、@Controller 等注释的类),或基于 Java 的 @Configuration 类中的 @Bean 方法。然后这些源在内部转换为 BeanDefinition 的实例,并用于加载整个 Spring IoC 容器实例。

3、Constructor-based or setter-based DI?(构造器注入还是Setter注入?)

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Required annotation on a setter method can be used to make the property be a required dependency; however, constructor injection with programmatic validation of arguments is preferable.

由于您可以混合使用基于构造函数和基于 setter 的 DI,因此根据经验,对强制依赖项使用构造函数,对可选依赖项使用 setter 方法或配置方法是一个很好的经验法则。请注意,在 setter 方法上使用 @Required 注释可用于使属性成为必需的依赖项;但是,最好使用带有参数编程验证的构造函数注入。

The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.

Spring 团队通常提倡构造函数注入,因为它可以让您将应用程序组件实现为 不可变对象,并确保所需的依赖项不为 null。此外,构造函数注入的组件总是以完全初始化的状态返回给客户端(调用)代码。作为旁注,大量的构造函数参数是一种糟糕的代码味道,这意味着该类可能有太多的责任,应该重构以更好地解决适当的关注点分离问题。

Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.

Setter 注入应该主要仅用于可以在类中分配合理默认值的可选依赖项。否则,必须在代码使用依赖项的任何地方执行非空检查。 setter 注入的一个好处是 setter 方法使该类的对象可以在以后重新配置或重新注入。因此,通过 JMX MBean 进行管理是 setter 注入的一个引人注目的用例。

4、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 可以将字符串格式提供的值转换为所有内置类型,例如 int、long、String、boolean 等。

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.Beans that are singleton-scope and set to be pre-instantiated(the default) are created when the container is created.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 are created and assigned.

Spring容器会在容器被创建的时候验证每个bean的配置,然而bean的这些属性在bean被实际创建之前都还没有被设置。默认情况下在容器创建时Bean是被创建为单例的,并设置为预实例化,因此,bean 仅在被请求时创建。Bean的创建可能会产生一个bean创建图, 描述bean 的依赖项及其依赖项的依赖项被创建和分配。

Circular dependencies
If you use predominantly constructor injection, it is possible to create an unresolveable circular dependency scenario.

For example : Class A requires an instance of class B through constructor injection,and class B requires an instance of class A through constructor injection.If you configure beans for classes A and B to be injected into each other,the Spring IoC container detects this circular reference at runtime,and throws a BeanCurrentlyInCreationException.

One possible solution is to edit the source code of some classes to be configured by setters rather than constructors.Alternatively, avoid constructor injection and use setter injection only.In other words,although it is not recommended,you can configure circular dependencies with setter injection.

Unlike the typical case(with no circular dependencies),a circular dependencies between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself(a classic chicken-and-egg scenario).

如果你主要使用构造器注入,可能会造成不能解析的循环依赖发生。
例如,类A通过构造器注入一个类B的实例,类B通过构造器注入一个类A的实例,如果你为类A和类B配置相互注入,Spring IOC容器在运行时会检测到这个循环引用,并抛出一个BeanCurrentlyInCreationException。
一个可行的解决方案是编写代码时使用setter注入而不是构造器注入,或者避免使用构造器注入并且只使用setter注入,也就是说,虽然不推荐,但是可以通过setter注入来配置循环依赖。
与典型情况(没有循环依赖)不同,bean A 和 bean B 之间的循环依赖迫使其中一个 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 sets properties and resolves dependencies as late as possible,when the bean is actually created.This means that a Spring container that has loaded corrctly 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 thorws an exception as a result of a missing or invalid property.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.

您通常可以相信 Spring 会做正确的事情。在容器加载时发现配置问题,例如引用了不存在的bean和循环依赖。Spring在实际创建bean时尽可能晚的设置bean的属性和解析bean的依赖。这意味着如果Spring容器已经正确的进行加载,当你请求一个存在问题的bean时,Spring会抛出异常。例如,抛出一个bean没有或错误属性值的异常。这可能会延迟一些配置问题的可见性是ApplicationContext实现默认预实例化单例bean的原因。在实际需要之前,以创建这些 bean 的一些前期时间和内存为代价,你会在创建 ApplicationContext 时发现配置问题,而不是稍后。

If no circular dependencies exist,when one or more collaborating beans are being injected into a dependent bean,each collaborating beans is totally configured prior to being injected into the dependent bean.This means that,if bean A has a denpendency on bean B,the Spring IoC container completely configures bean B prior to invoking the setter method on bean A.In other words,the bean is instantiated(if it is not a pre-instantiated singleton),its dependencies are set,and the relevant lifecycle methods are invoked.

如果没有循环依赖存在,当一个或多个相互协作的bean被注入到依赖它们的bean中,每一个相互协作的bean会被优先配置和注入到这个依赖它们的bean当中去。这意味着,如果beanA依赖beanB,Spring IOC容器会优先配置bean B并且通过调用bean A的setter方法将bean B注入,换句话说,bean 被实例化(如果它不是预先实例化的单例),它的依赖项被设置,并且相关的生命周期方法被调用。

二、Dependencies and Configuration in Detail (详解依赖和配置)

As mentioned in the previous section,you can define bean properties and constructor arguments as references to other managed beans or as values defined inline.Spring’s XML-based configuration metadata supports sub-element type within its <property/> and <constructor-tag/> elements for this purpose.

正如前面章节所提到的,你可以定义 bean的 属性和构造函数参数引用其他被管理的bean或者行内值作为它们的值。在Spring 基于 XML 的配置元数据中支持<property/> 和 <constructor-tag/> 子元素类型来达到此目的。

1、Straight Values 直接值(如原始数据类型,字符串等)

The value attribute of 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:

<property/> 元素的 value 属性指定bean的属性或构造函数参数值为人类可读的字符串表示形式。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 succint XML configuration:

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

You can also configure a java.util.Properties instance, as follows:

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

Spring 容器使用 JavaBeans PropertyEditor 机制将 元素内的文本转换为 java.util.Properties 实例。这是一个很好的捷径,并且是 Spring 团队支持使用嵌套 <value/> 元素而不是 value 属性样式的少数几个地方之一。

2、The idref element(idref元素)

The idref element is simply an error-proof way to pass the id 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 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.
第一种形式比第二种形式更可取,因为使用 idref 标签让容器在部署时验证引用的命名 bean 实际存在。在第二种变体中,不执行验证。

3、References to Other Beans(引用其他bean)

The ref element is inside a <constructor-tag/> or <property/> definition element.Here,you set the value of the specified property of a bean to be a reference to another bean managed by the container.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. (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.

ref元素是<constructor/>或<property/>元素的一个子元素,通过这个元素你能够将一个被容器管理的bean作为值,来达到依赖设置的目的。作用范围和验证取决于你是否通过 bean 或 parent 属性指定其他对象的 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 元素:

<ref bean="someBean"/>

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

通过 parent 属性指定目标 bean 可创建当前容器对父容器中的 bean 的引用。parent 属性的值可能与目标 bean 的 id 属性或目标 bean 的 name 属性中的值之一相同。目标 bean 必须在当前容器的父容器中。你应该主要在具有容器层次结构并且希望使用与父 bean 同名的代理将现有 bean 包装在父容器中时使用此 bean 引用变体。以下代码清单显示了如何使用 parent 属性:

<!-- in the parent context -->
<bean id="accountService" class="com.something.SimpleAccountService">
    <!-- insert dependencies as required as 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>

4、Inner Beans(内部bean)

A <bean/> element inside the <property/> or <constructor/> elements defines an inner bean,as the following example shows:

<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 independency or to inject them into collaborating beans other than into the enclosing bean.

定义一个内部bean不需要为其定义ID或者name,如果定义了,容器也不会将其作为标识符并且忽略作用域标识符。因为内部bean总是是匿名的并且随外部bean的创建而被创建。不可能访问内部 bean 的依赖或将它们注入到除外部 bean 之外的其他 bean 中进行协作。

5、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/> 和\ 元素分别设置 Java 集合类型 List、Set、Map 和 Properties 的属性和参数。以下示例显示了如何使用它们:

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

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.
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 child Properties collection’s value set inherits all property elements from the parent , and the child’s value for the support value overrides the value in the parent collection.

请注意在子 bean 定义的 adminEmails 属性的 <props/> 元素上使用了 merge=true 属性。当子bean被解析和实例化时,结果实例有一个 adminEmails Properties 集合,其中包含将子级的 adminEmails 集合与父级的 adminEmails 集合合并的结果。子 Properties 集合的值集继承了父 的所有属性元素,支持值的子值覆盖父集合中的值。

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 definitions is redundant and does not result in the desired merging.
你不能合并不同的集合类型(例如 Map 和 List)。如果你试图这么做,将会抛出异常。必须在较低的继承子定义上指定合并属性。在父集合定义上指定合并属性是多余的,不会导致所需的合并。

Strongly-typed collection
With the introduction of generic types in java 5,you can use strongly typed collections.That is,it is possible to declare a collection type such that it can only contain String elements.If you use Spring to dependency-injection a stongly-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 5 中泛型类型的引入,您可以使用强类型集合。也就是说,可以声明一个集合类型,使其只能包含 String 元素。如果您使用 Spring 将强类型集合依赖注入到 bean 中,您可以利用 Spring 的类型转换支持,以便在将强类型集合实例的元素添加到集合之前将其转换为适当的类型。以下java代码和XMl显示了如何实现:

public class SomeClass {

    private Map<String, Float> accounts;

    public void setAccounts(Map<String, Float> accounts) {
        this.accounts = accounts;
    }
}
<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 account 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 value are converted into an actual Float type.

当something bean 的account 属性准备注入时,强类型Map<String,Float> 的元素类型的泛型信息可以通过反射获得。因此,Spring 的类型转换基础功能将各种值元素识别为 Float 类型,并将字符串值转换为实际的 Float 类型。

6、Null and Empty String Values(Null和空字符串值)

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对待空的属性参数会作为空字符串,以下基于 XML 的配置元数据片段将电子邮件属性设置为空字符串值 ("")。

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

The <null/> element handles null values. The following listing shows an example:

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

三、Using depends-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 <ref/> element in XML-based configuration metadata.However,sometimes dependencies between bean 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依赖其他的bean,这通常意味着一个 bean 被设置为另一个 bean 的属性。在基于XML的元数据配置中通常使用<ref/>元素来完成依赖设置。不过有时候依赖和bean之间没有直接的关联,例如一个类中的静态初始化器需要被触发,数据库驱动的注册。depends-on属性能够指明在bean初始化前先强制要求有哪些bean需要被先初始化。以下示例使用depends-on 属性来表达对单个bean 的依赖:

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

To express a denpendency on multipile beans,supply alist 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 given bean are destroyed first,prior to the given bean itself being destroyed.Thus,depends-on can also control shutdown order.

depends-on属性可以指定初始化时依赖且只在单例bean的情况下可以指定相应的销毁时间依赖项。与给定 bean 定义依赖关系的从属 bean ,在给定 bean 本身被销毁之前首先被销毁。因此,depends-on 也可以控制关​​闭顺序。

四、Lazy-initialized Beans(懒加载bean)

By default,ApplicationContext implementations eagerly create and configure all singleton beans as part of the initialization process.this pre-instantiation is desirable,becuase errors in the configuration or surrounding enviroment 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:

默认情况下,ApplicationContext急切的在bean的初始化部分就创建和配置所有单例bean。这种预实例化是明智的,因为这样配置文件的错误信息会被立即发现,而不是几小时或者几天后。当这种预实例的行为不合适时,你可以在bean的配置中配置懒加载,阻止这个单例bean的预实例化。懒加载告诉IOC容器当bean被首次请求时才创建bean实例,而不是在开始。
在 XML 中,此行为由\ 元素上的 lazy-init 属性控制,如以下示例所示:

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

当 ApplicationContext 使用前面的配置时,lazy bean 不会在启动时被预先实例化,而 not.lazy bean 会被预先实例化。

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.

然而,当一个懒加载的bean是一个不是懒加载的bean的依赖时,ApplicationContext还是会在启动时加载这个bean,因为要满足这个依赖就必须如此。懒加载的 bean 被注入到其他地方不是懒加载的单例 bean 中。

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:
你也可以在容器级别上通过使用<bean/>元素的default-lazy-init属性来控制懒加载,如以下示例所示:

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

五、自动装配各个协同者

The Spring container can autowire relationships between collaborating beans.You can let Spring resolve collaborators automatically for your bean by inspecting the contents of the ApplicationContext.Autowiring has the following advantages:

Spring容器能够自动装配有协作关系的各个bean。你可以让 Spring 通过检查 ApplicationContext 的内容自动为你的 bean 解析协作者。自动注入有以下优点:

  • Autowiring can significantly reduce the need to specify properties or constructor arguments.自动注入能够显著减少需要指明的属性或构造器参数。
  • Autowiring can update a configuration as your objects evovle.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, 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 指定自动装配,因此可以选择要自动装配的那些。下表描述了四种自动装配模式:

ModeExplanation
no(Default) No autowiring
byNameAutowiring by property name.
byTypeLets 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. 如果容器中恰好存在一个属性类型的 bean,则让属性自动装配。如果存在多个,则抛出致命异常,这表明你不能为该 bean 使用 byType 自动装配。如果没有匹配的 bean,则什么也不会发生。
constructorAnalogous 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,则会引发致命错误。

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.Consider the limitations and disadvantages of autowiring:

自动装配在整个项目中一致使用时效果最佳。如果通常不使用自动装配,开发人员可能会使用它来连接一两个 bean 定义,这可能会让人感到困惑。自动装配有以下限制和缺点:

  • 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.
    以属性和构造器方式注入的依赖会覆盖自动注入依赖。不能自动注入原始数据类型的数据,例如字符串、数组。
  • 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 管理的对象之间的关系不再明确记录。
  • 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.
    容器中可能有多个bean定义和setter、constructor中需要被自动注入的参数匹配。对于数组、集合容器来说这不是一个严重的问题。然而对于明确只依赖一个对象的情况下,这种含糊不清不会被随意解决,如果没有唯一的bean定义可用,将抛出异常。

Excluding a Bean from Autowiring

On a per-bean basis, you can exclude a bean from autowiring. In Spring’s XML format, set the autowire-candidate attribute of the element to false. The container makes that specific bean definition unavailable to the autowiring infrastructure (including annotation style configurations such as @Autowired).

对于每一个bean,你可以在自动装配时排除它。Spring 的 XML 格式,将 <bean/> 元素的 autowire-candidate 属性设置为 false。容器会使该特定 bean 定义对自动装配功能不可用(包括注释样式配置,例如 @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 没有标记为自动装配候选者,也会被解析。因此,如果名称匹配,按名称自动装配就会注入这个 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 属性接收一到多个匹配模式。例如,要限制自动装配候选bean的name以Repository结尾时,使用*Repository即可。如果有多个模式,各个模式之间使用逗号分割即可,bean 定义中的 autowire-candidate 属性的显式 true 或 false 值始终优先。对于此类 bean,模式匹配规则不适用。

Method Injection
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. 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都是单例的。当一个单例 bean 需要与另一个单例 bean 协作或一个非单例 bean 需要与另一个非单例 bean 协作时,通常通过将一个 bean 定义为另一个 bean 的属性来处理依赖关系。当 bean 生命周期不同时就会出现问题。假设单例 bean A 需要使用非单例(原型)bean B,也许在 A 上的每个方法调用上。容器只创建单例 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 接口使 bean A 知道容器,并通过对容器进行 getBean(“B”) 调用,在每次 bean A 需要时请求(一个典型的新)bean B 实例。以下示例显示了这种方法:

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

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 Framework。方法注入是 Spring IoC 容器的一个有点高级的特性,可以让你干净地处理这个用例。

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 invovles a prototype bean.The Spring Framework implements this method injection by using bytecode generation from CGLIB library to dynamically generate a subclass that overrides the method.
lookup method injection是容器覆盖容器管理 bean 中的方法并返回容器中另一个命名 bean 查找结果的能力。查找通常涉及原型 bean。Spring 框架通过使用来自 CGLIB 库的字节码生成来动态生成覆盖该方法的子类来实现此方法注入。

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:

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();
}

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:
如果该方法是抽象的,则动态生成的子类将实现该方法。否则,动态生成的子类会覆盖原始类中定义的具体方法。考虑以下示例:

<!-- 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();
}

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) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值