1.4. Dependencies 依赖关系
A typical enterprise application does not consist of a single object (or bean in the Spring parlance). Even the simplest application has a few objects that work together to present what the end-user sees as a coherent application. This next section explains how you go from defining a number of bean definitions that stand alone to a fully realized application where objects collaborate to achieve a goal.
典型的企业应用程序不是由单个对象(或者用Spring的说法是bean)组成的。即使是最简单的应用程序也有一些对象协同工作,以呈现最终用户所认为的一致应用程序。这一节将解释如何从定义多个独立的bean定义到一个完全实现的应用程序,在这个应用程序中,对象协作以实现目标。
1.4.1. Dependency Injection(DI,依赖注入)
Dependency injection (DI) is a process whereby objects define their dependencies (that is, the other objects with which they work) 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原则,代码更简洁,当对象与其依赖项一起提供时,去耦更有效。对象不查找其依赖项,并且不知道依赖项的位置或类。因此,您的类变得更容易测试,特别是当依赖关系位于接口或抽象基类上时,这允许在单元测试中使用存根或模拟实现。
DI exists in two major variants: Constructor-based dependency injection and Setter-based dependency injection.
DI存在于两个主要变体中:基于构造函数的依赖注入
和基于Setter的依赖注入
。
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 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...
}
Notice that there is nothing special about this class. It is a POJO that has no dependencies on container specific interfaces, base classes or annotations.
注意这个类没有什么特别之处。它是一个POJO,它不依赖于特定于容器的接口、基类或注释。
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时将这些参数提供给适当的构造函数的顺序。考虑以下类别:
package x.y;
public class ThingOne {
public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
// ...
}
}
Assuming that 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 <constructor-arg/>
element.
假设ThingTwo和thingtree类没有继承关系,则不存在潜在的歧义。因此,以下配置工作正常,您不需要在<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>
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 <value>true</value>
, Spring cannot determine the type of the value, and so cannot match by type without help. Consider the following class:
引用另一个bean时,类型是已知的,并且可以进行匹配(就像前面的例子一样)。当使用简单类型时,例如<value>true</value>,Spring无法确定值的类型,因此在没有帮助的情况下无法按类型进行匹配。考虑以下类别:
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private int years;
// The Answer to Life, the Universe, and Everything
private 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>
In addition to resolving the ambiguity of multiple simple values, specifying an index resolves ambiguity where a constructor has two arguments of the same type.
除了解决多个简单值的模糊性外,指定索引还可以解决构造函数具有两个相同类型参数的模糊性。
The index is 0-based. | |
---|---|
索引从0开始。 |
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>
Keep in mind that, to make this work out of the box, your code must be compiled with the debug flag enabled so that Spring can look up the parameter name from the constructor. If you cannot or do not want to compile your code with the debug flag, you can use the @ConstructorProperties JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:
请记住,要使这项工作开箱即用,必须在编译代码时启用调试标志,这样Spring才能从构造函数中查找参数名。如果您不能或不想使用debug标志编译代码,可以使用@ConstructorProperties JDK注释显式地命名构造函数参数。然后示例类必须如下所示:
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
下一节(待续。。。)
Setter-based Dependency Injection基于Setter的依赖注入