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.
基于Setter的DI是由容器在调用无参数构造函数或无参数静态
工厂方法实例化bean后调用bean上的Setter方法来实现的。
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类注入的纯依赖项。这个类是传统的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
定义、带注释的组件(即用@Componen
、@Controller
等注释的类)或基于Java的 @Configuration
类中的 @bean
方法。然后,这些源代码在内部转换为BeanDefinition
的实例,并用于加载整个SpringIoC容器实例。
Constructor-based or setter-based DI?
基于构造函数还是基于setter的DI?
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团队通常提倡构造函数注入,因为它允许您将应用程序组件实现为不可变的对象,并确保所需的依赖项不为空。此外,构造函数注入的组件总是以完全初始化的状态返回给客户端(调用)代码。顺便说一句,大量的构造函数参数是一种糟糕的代码气味,这意味着类可能有太多的责任,应该进行重构以更好地解决关注点的正确分离。
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注入应该主要用于可选的依赖项,这些依赖项可以在类中分配合理的默认值。否则,必须在代码使用依赖项的任何地方执行not null检查。setter注入的一个好处是setter方法使该类的对象能够在以后重新配置或重新注入。因此,通过jmx mbean进行管理是setter注入的一个引人注目的用例。
Use the DI style that makes the most sense for a particular class. Sometimes, when dealing with third-party classes for which you do not have the source, the choice is made for you. For example, if a third-party class does not expose any setter methods, then constructor injection may be the only available form of DI.
使用对特定类最有意义的DI样式。有时,在处理第三方类时,如果您没有源代码,则会为您做出选择。例如,如果第三方类不公开任何setter方法,那么构造函数注入可能是DI的唯一可用形式。
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-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. Note that resolution mismatches among those dependencies may show up late — that is, on first creation of the affected bean.
Spring容器在创建容器时验证每个bean的配置。但是,在实际创建bean之前,不会设置bean属性本身。在创建容器时,将创建为单列作用域并设置为预实例化(默认值)的bean。作用域是在Bean作用域中定义的。否则,只有在请求时才会创建bean。创建bean可能会导致创建一个bean图(查看算法:深度克隆图节点),因为bean的依赖项及其依赖项(等等)被创建和分配。请注意,这些依赖项之间的解析不匹配可能会出现在较晚的情况—— ,也就是说,在第一次创建受影响的bean时。
Circular dependencies
循环依赖项
If you use predominantly constructor injection, it is possible to create an unresolvable 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
.
例如:类A通过构造函数注入需要一个类B的实例,而B类通过构造函数注入需要一个类A的实例。如果为类A和类B配置bean以相互注入,springioc容器将在运行时检测到这个循环引用,并抛出一个beancurrentlyIncrementationException。
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.
一种可能的解决方案是编辑一些类的源代码,这些类由setter而不是构造函数配置。或者,避免构造函数注入,只使用setter注入。换句话说,尽管不建议这样做,但您可以使用setter注入配置循环依赖项
。
Unlike the typical case (with no circular dependencies), a circular dependency 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).
与典型的情况(没有循环依赖)不同,BeanA和BeanB之间的循环依赖迫使一个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 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. 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 pre-instantiated.
你可以相信Spring会做正确的事情。它在容器加载时检测配置问题,例如对不存在的bean的引用和循环依赖关系。当实际创建bean时,Spring设置属性并尽可能晚地解析依赖项。这意味着如果在创建对象或它的某个依赖项时出现问题,那么正确加载的Spring容器以后可以在请求对象时生成异常。例如,由于缺少属性或属性无效,bean抛出异常。某些配置问题的潜在延迟可见性就是ApplicationContext
实现在默认情况下
预先实例化 singleton bean
的原因。在实际需要这些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. 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. 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.
如果不存在循环依赖关系,那么当一个或多个协作bean被注入到依赖bean中时,每个协作bean在被注入到依赖bean之前被完全配置。这意味着,如果BeanA对BeanB有依赖关系,那么SpringIoC容器在调用BeanA上的setter方法之前完全配置了BeanB。换句话说,bean被实例化(如果它不是预先实例化的单例),它的依赖关系被设置,并调用相关的生命周期方法(例如配置的init
方法或initializationBean
回调方法)。
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的DI。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;
}
}
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文件中指定的属性相匹配。以下示例使用基于构造函数的DI:
<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;
}
}
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调用静态工厂方法来返回对象的实例,而不是使用构造函数:
<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;
}
}
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/>
元素提供,与实际使用构造函数时完全相同。工厂方法返回的类的类型不必与包含静态工厂方法的类具有相同的类型(尽管在本例中是这样)。实例(非静态)工厂方法可以以本质上相同的方式使用(除了使用factory-bean
属性而不是class
属性),因此我们不在这里讨论这些细节。
下一节:Dependencies and Configuration in Detail 详细的依赖关系和配置