Spring FrameWork Core Tech

The IoC Container 1.4.1

2019-02-14  本文已影响0人  小鲍比大爷

1.4. Dependencies

面向对象的系统都会被分解成多个协作的对象,通过对象间的协作系统才能正常运行,对象间会有依赖关系,而IoC Container则帮助我们管理这些对象间的依赖关系。从代码上来讲,本质还是帮助生成bean以及组装bean所需要的依赖对象。

1.4.1. Dependency Injection

依赖注入的优点(代码简洁、对象间解耦):

Code is cleaner with the DI principle, and decoupling is more effective when objects are provided with their dependencies.

DI注入的两种主要方式(基于构造函数、基于setter方法):

DI exists in two major variants: Constructor-based dependency injection and Setter-based dependency injection.

Constructor-based Dependency Injection(基于构造函数的依赖注入):

package x.y;

public class ThingOne {

    public ThingOne(ThingTwo thingTwo, ThingThree 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>

假设构造函数中包含基本类型,应该使用下面三种方式进行配置。
Constructor argument type matching(指定参数类型的配置):

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;
    }
}
<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>

Constructor argument 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.
需要注意一点,下标从0开始。

Constructor argument name(使用参数名称指定,同样不会产生歧义):

<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 @ConstructorProperties JDK annotation to explicitly name your constructor arguments. The sample class would then have to look as follows:

package examples;

public class ExampleBean {

    // Fields omitted

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

接下来是基于setter方法的依赖注入。
Setter-based Dependency Injection
配置示例:

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

下面是使用构造函数初始化ExampleBean的配置方法:

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

再给出一个使用工厂函数返回实例的初始化配置方式:

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

到底是选择构造函数注入还是setter方法注入呢?
官方推荐使用构造函数方式注入,在两种情况下使用setter方法:1,循环依赖的情况,A依赖B,B依赖A;2,依赖对象可选的情况,意思是该依赖对象为空也无所谓。
使用构造函数的优点在于:需要遵循构造函数的参数注入相应的依赖对象,这样可以避免对象为空,所以在依赖对象必须强制存在的情况下,使用构造函数可以有效的提示用户该依赖注入对象不能为空。另外,使用构造函数,另外一个优点是可以将类中的变量定义为不可变类型(final关键字)。最后,在引用第三方库的时,经常没有setter方法,那么就只能使用构造函数注入第三方库的对象了。

上一篇下一篇

猜你喜欢

热点阅读