Setter-based dependency injection

来源:互联网 发布:知乎清华毕业 编辑:程序博客网 时间:2024/06/05 08:14

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 no-argument static factory method to instantiate your bean.


基于Setter 的DI 指的是容器在 通过无参 构造器或者 无参构造工厂放来实例化bean后调用你的beans 的setter地方法完成的。


The following example shows a class that can only be dependency-injected 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 with PropertyEditor instances to convert properties from one format to another. However, most Spring users do not work with these classes directly (i.e., programmatically) but rather with XML bean definitions, annotated components (i.e., classes annotated with @Component, @Controller, etc.), 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的依赖注入,还支持在使用构造器完成依赖注入后在使用setter进行属性的添加,你使用 BeanDefinition 形式以及 用来进行形式转换的 PropertyEditor实例 来配置依赖项,然而,多数Spring 用户不直接操作这些类,而倾向与使用XML bean,组件注解或者或者基于java 的@configuration 下的@bean修饰的方法,然后这些资源会被内部转换成BeanDefinition实例然后由Ioc容器进行加载。

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 a required dependency.


由于你可以混合构造方法和setter,那么请使用构造函数进行强制性的赖幸注入,使用 setter 或者 configuration 方法 进行 可选依赖的加载,注意,使用 Setter 方法的 @Required 注解可以用来将依赖关系标识为必要的。

The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to 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 注入主要用与 可选的额依赖。可以为该类分配合理的默认值,另外,必须在使用依赖关系的所有位置进行非空检测,Setter 注入的一个好处,是该类的对象可以在后面再次进行 配置或者重新注入,JMX的Bean就是在编译时使用set方法注入的引入注目的例子

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方法是,那么构造注入是唯一的依赖注入形式

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 via XML, Java code, or annotations.
  • For each bean, its dependencies are expressed in the form of properties, constructor arguments, or arguments to the static-factory method if you are using that instead of a normal constructor. These dependencies are provided to the bean, when the bean is actually created.
  • Each property or constructor argument is an actual definition of the value to set, or a reference to another bean in the container.
  • Each property or constructor argument which 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, etc.

  • ApplicationContext 根据配置元数据的描述进行创建以及实例化,配置元数据可以是XML Java 代码或者注解等形式
  • 对于每个bean ,依赖可以被看成一种属性,你可以使用 构造喊得的参数或者参数的静态工厂方式的参数来替代常规的构造方法。当bean被创建的时候,这些依赖会被提供给bean。
  • 每个属性或者构造参数都是要设置值的实际定义,或者其他容器中其他bean的引用
  • 每个属性或者构造参数都会被指定的形式转换成实际的属性或者构造函数参数。默认情况下,Spring 能将提供的 String 类型值,转换成所有的内置类型,比如 int 、longString、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 Section 3.5, “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, i.e. on first creation of the affected bean.


容器在创建的时候会被每个bean都进行验证,但是,bean在实际创建之前都不会设置bean属性。容器创建的时候,被设置为预实例化的单例beans会被常见。 Scope 在第3.5 节中定义.其他的类都只会在需要的时候才进行创建。一个bean的创建可能会导致他依赖的bean被创建。在依赖中不匹配的resolution将会在后面展示,例如受影响的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类实例,如果你将AB 类的 bean配置为 彼此注入,Spring IoC容器会在运行时,检测到 循环依赖,并且抛出一个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.


一个可能的解决方案就是,使用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/egg scenario).


循环依赖的常见,会产生 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 which 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 will lazy-initialize, rather than be pre-instantiated.


你可以信任Spring 会做出正确的事情,他会在容器加载的同时,检测配置问题,比如引用了不存在的beans 以及 循环依赖等。 在bean被真正创建的时候,Spring 会尽可能玩的 设置属性以及解决依赖。这就意味着,整理加载的容器会在 你请求有问题的对象(创建或者处理依赖时出现了问题),抛出异常。例如: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 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在被注入到依赖bena之前被完全配置,也就是说,bean A 对bean B有依赖关系的时候,Ioc 容器会在调用bean A 的之前,完成配置Bean B 或者说时 实例化bean B。 它设置依赖关系,以及调用相关的生命周期方法。(如,配置的init方法。或者 InitializingBean 回掉方法)

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:


下面的例子使用基于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"/>
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:


上面的例子中,xml文件中定义的属性与setters 声明的方法相匹配。下面的例子使用了构造器的依赖注入。

<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"/>
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 will be 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"/>
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 via 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 would be used in an essentially identical fashion (aside from the use of the factory-bean attribute instead of the class attribute), so details will not be discussed here.

通过 节点向 静态工厂方法提供参数,与使用构造器完全一样,工厂方法返回的类的类型不一定非要和包含静态类的类型一样,尽管上面例子中的一样。实例工厂方法与之类型,此处不在讨论。

原创粉丝点击