Spring之(二)依赖注入(控制反转)

二、依赖注入(控制反转)

2.1 Spring框架的核心功能

  • Spring容器作为超级大工厂,负责创建、管理所有的Java对象,这些Java对象被称为Bean;
  • Spring容器管理容器中Bean之间的依赖关系,Spring使用一种被称为“依赖注入”的方式来管理Bean之间的依赖关系。

2.2 理解依赖注入

不管是依赖注入还是控制反转,其含义相同。当某个Java对象(调用者)需要调用另一个Java对象(被依赖对象)的方法时,在传统模式下通常有两种做法:

  1. 原始做法:调用者主动创建被依赖对象,然后再调用被依赖对象的方法;

  2. 简单工厂模式:调用者先找到被依赖对象的工厂,然后主动通过工厂去获取被依赖对象,最后再调用被依赖对象的方法。
    上述的“主动”二字,必然会导致调用者与被依赖对象实现类的硬编码耦合,非常不利于项目升级的维护。使用Spring框架之后,调用者无需主动获取被依赖对象,调用者只要被动接收Spring容器为调用者的成员变量赋值即可。由此可见,使用Spring后,调用者获取被依赖对象的方式由原来的主动获取变成了被动接受,所以依赖注入又称为控制反转。

    所谓的依赖注入,就是将组件的构建和使用分开。
    在这里插入图片描述

2.3 依赖注入的概念

在使用Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,Spring容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由应用代码转移到了Spring容器,控制权发生了反转,这就是Spring的控制反转。

从Spring容器的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量,这相当于为调用者注入了它依赖的实例:

2.4 依赖注入的两种方式

  • 设值注入(setter方式注入)
    设值注入就是指IOC容器使用setter方法注入被依赖的实例。通过调用无参构造器或无参静态工厂实例化Bean后,调用该Beandesetter方法,即可实现基于setter方法的依赖注入。

    <bean id="hy" class="cn.spring.iface.Hanyu"/>
    <!--依赖注入2:set方法注入-->
    <bean id="am" class="cn.spring.iface.American">
    <!--        <property name="lan" ref="yy"/>-->
        <property name="lan" ref="hy"/>
    </bean>
    
  • 构造方法注入
    指IoC容器使用构造方法注入被依赖的实例。基于构造方法的依赖注入通过调用带参数的构造方法来实现,每个参数代表着一个依赖。
    利用构造器来设置依赖关系的方式,被称为构造注入。通俗来说,就是驱动Spring在底层以反射方式执行带指定参数的构造器,当执行带参数的构造器时,就可利用构造器参数对成员变量执行初始化——这就是构造注入的本质。

    <bean id="hy" class="cn.spring.iface.Hanyu"/>
    <bean id="yy" class="cn.spring.iface.English"/>
    <!--依赖注入1:构造注入-->
    <bean id="ch" class="cn.spring.iface.Chinese">
        <constructor-arg ref="hy"/>
    </bean>
    

为了便于理解,直接上代码:
有两个接口Human和Language和其对应的四个实现类:

//Human接口
package cn.spring.face;
public interface Human {
    void eat();
    void walk();
    void speak();
}
//Language接口
package cn.spring.face;
public interface Language {
    public String kind();
}


/**
* Human的实现类:Chinese和American 
*/
//Chinese
package cn.spring.iface;
import cn.spring.face.Human;
import cn.spring.face.Language;
public class American implements Human {
    private Language lan;
    //set方法用于依赖注入
    public void setLan(Language lan) {
        this.lan = lan;
    }
    public American() {
    }
    @Override
    public void eat() {
        System.out.println("美国人吃西餐...");
    }
    @Override
    public void walk() {
        System.out.println("美国人喜欢坐车...");
    }
    @Override
    public void speak() {
        System.out.println("我是美国人,我会说"+lan.kind());
    }
}
//American 
package cn.spring.iface;
import cn.spring.face.Human;
import cn.spring.face.Language;
public class Chinese implements Human {
    private Language lan;
    //set方法用于依赖注入
    public void setLan(Language lan) {
        this.lan = lan;
    }
    public Chinese(Language lan) {
        this.lan = lan;
    }
    public Chinese() {
    }
    @Override
    public void eat() {
        System.out.println("舌尖上的中国,中国人很会吃...");
    }
    @Override
    public void walk() {
        System.out.println("中国人喜欢散步...");
    }
    @Override
    public void speak() {
        System.out.println("我是中国人,我会说"+lan.kind());
    }
}


/**
*  Language的实现类:English和hanyu
*/
//English
package cn.spring.iface;
import cn.spring.face.Language;
public class English implements Language {
    @Override
    public String kind() {
        return "英语";
    }
}
//hanyu
package cn.spring.iface;
import cn.spring.face.Language;
public class Hanyu implements Language {
    @Override
    public String kind() {
        return "汉语";
    }
}


//applicationContext.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="hy" class="cn.spring.iface.Hanyu"/>
    <bean id="yy" class="cn.spring.iface.English"/>
    <!--依赖注入1:构造注入-->
    <bean id="ch" class="cn.spring.iface.Chinese">
        <constructor-arg ref="hy"/>
    </bean>
    <!--依赖注入2:set方法注入-->
    <bean id="am" class="cn.spring.iface.American">
<!--        <property name="lan" ref="yy"/>-->
        <property name="lan" ref="hy"/>
    </bean>
</beans>


//测试类test
package cn.spring.test;
import cn.spring.face.Human;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.FileSystemResource;
public class test2 {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new FileSystemXmlApplicationContext("src/applicationContext.xml");
        Human human = (Human) applicationContext.getBean("am");
        human.eat();
        human.speak();
    }
}

注意:
美国人会说英语,但是也可能会说汉语。在实现接口Language的方法kind()中,我们可以直接输出会说的语言,但是当一种人会说的语言过多时我们必须一个一个进行配置,这时候就会出现代码耦合现象,非常不利于代码的维护,所以我们就利用依赖注入的方式,在Human的实现类中动态注入,这时,我们只需要修改xml配置文件中的注入类的属性值即可。

2.4 两种注入方式的对比

  • 设值注入有如下优点:

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

    • 构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入。
    • 对于依赖关系无需变化的Bean,构造注入更有用处。因为没有setter方法,所有的依赖关系全部在构造器内设定,无须担心后续的代码对依赖关系产生破坏。
    • 依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系,对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值