一、说到依赖注入(控制反转),先要理解什么是依赖。

Spring 把相互协作的关系称为依赖关系。假如 A 组件调用了 B 组件的方法,我们可称A 组件依赖于 B 组件


二、什么是依赖注入。

在传统的程序设计过程中,通常由调用者来创建被调用者的实例

在依赖注入的模式下,创建被调用者的工作不再由调用者来完成,因此称为控制反转;创建被调用者实例的工作通常由Spring 容器来完成,然后注入给调用者,因此也称为依赖注入


三、依赖注入的好处。

依赖注入让 Spring 的 Bean 以被指文件组织在一起,而不是以硬编码的方式耦合在一起程序完成无须理会被调用者的实现,也不无须主动定位工厂,这是最好的解耦方式实例之间的依赖关系由 IoC 容器负责管理

四、依赖注入的 Spring 实现

1、设值注入

设值注入是指 IoC 容器使用属性的 setting 方法来注入被依赖的实例。


先创建一个实体对象(Bean)
public class HelloWorld {  
    private String msg;  
  
    public String getMsg() {   
        return msg;  
    }  
    public void setMsg(String msg) {  
        this.msg = msg;  
    }  
} 

再配置文件applicationContext.xml,实例化bean 

<bean id="helloBean" class="com.spring.demo.HelloWorld">  
       <property name="msg" value="Hello World!"/>  
 </bean> 

最后测试是否能够得到注入的bean,并打印出对象的属性。 

public static void main(String[] args){  
        //读取配置文件,获得BeanFactory  
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
        BeanFactory factory = context;  
          
        HelloWorld hello = (HelloWorld)factory.getBean("hello");  
          
        System.out.println(hello.getMsg());   
 } 

2、构造注入

除了设值注入,还有另一种注入方式,这种方式在构造实例时,已为其完成了依赖关系的初始化。这种利用构造器来设置依赖关系的方式,被称为构造注入。

先创建一个实体对象(Bean)

public class HelloWorld {  
    private String msg;  
  
    //需要一个默认无参构造器  
    public HelloWorld(){}  
      
    public HelloWorld(String msg){  
        this.msg = msg;  
    }  
      
    public String getMsg() {  
        return msg;  
    }  
    public void setMsg(String msg) {  
        this.msg = msg;  
    }  
} 


再配置文件applicationContext.xml,实例化bean。

<bean id="hello" class="com.spring.demo.HelloWorld">    
     <constructor-arg index="0">   
           <value>HelloWorld!</value>   
       </constructor-arg>    
 </bean> 


最后测试是否能够得到注入的bean,并打印出对象的属性。

public static void main(String[] args){  
        //读取配置文件,获得BeanFactory  
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");  
        BeanFactory factory = context;  
          
        HelloWorld hello = (HelloWorld)factory.getBean("hello");  
          
        System.out.println(hello.getMsg());   
 } 

五、处理bean依赖关系的步骤

1、根据定义bean的配置创建并初始化BeanFactory实例 
2、每个bean的依赖将以属性、构造器参数、或静态工厂方法参数的形式出现。当这些bean被实际创建时,这些依赖也将会提供给该bean。 
3、每个属性或构造器参数既可以是一个实际的值,也可以是对该容器中另一个bean的引用。 
4、每个指定的属性或构造器参数值必须能够被转换成特定的格式或构造参数所需的类型。 

Spring会在容器被创建时验证容器中每个bean的配置,包括验证那些bean所引用的属性是否指向一个有效的bean。在bean被实际创建之前,bean的属性并不会被设置。伴随着bean被实际创建,作为该bean的依赖bean以及依赖bean的依赖bean也将被创建和分配。



六、两种注入方式的对比

1、相比之下,设值注入具有如下的优点:

(1)、与传统的 JavaBean 的写法更相似,程序开发人员更容易理解、接受。通过 Setting 方法设定依赖关系显得更加直观、自然。
(2)、对于复杂的依赖关系,如果采用构造注入,会导致构造过于臃肿,难以阅读。Spring 在创建 Bean 实例时,需要同时实例化其依赖的全部实例,因而导致性能下降。而使用设值注入,则能避免这些问题。
(3)、尤其是在某些属性可选的情况下,多参数的构造器更加笨重。

2、构造注入也不是绝对不如设值注入,在某些特定的场景下,构造注入比设值注入更优秀。构造注入也有如下优势:

(1)、构造注入可以在构造器中决定依赖关系的注入顺序,有限依赖的优先注入。例如,组件中某些其他依赖关系的注入,尝尝需要依赖于 Datasource 的注入。采用构造注入,可以在代码中清晰地决定注入顺序。
(2)、对于依赖关系无须变化的 Bean ,构造注入更有用处。因为没有 setting 方法,所有的依赖关系全部在构造器内设定。因此,无须担心后续代码对依赖关系产生的破坏。
(3)、依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系。对组件的调用者而言,组件内部的依赖关系完成透明,更符合高内聚的原则。

两种方式总结:建议采用以设值注入为住,构造注入为辅的注入策略。对于依赖关系无须变换的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用设值注入


参考资料:

《轻量级 Java EE 企业应用实战(第三版)》 李刚

http://ganshisheng.iteye.com/blog/438608












Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐