IoC和DI以及它们的区别

一、IoC

  1. IOC(Inverse of Control):控制反转,它不是什么技术,而是一种设计思想。在Java开发中,IoC意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

  2. 下面从哪些方面被反转了来理解IoC?
    1、谁控制谁?为什么叫反转? ------ IoC容器控制,而以前是应用程序控制,所以叫反转
    2、控制什么? ------ 控制应用程序所需要的资源(对象、文件……)
    3、为什么控制? ------ 解耦组件之间的关系
    4、控制的哪些方面被反转了? ------ 程序的控制权发生了反转:从应用程序转移到了IoC容器。

  3. IoC容器特点
    无需主动new对象;由IoC容器创建。
    不需要主动装配对象之间的依赖关系,而是描述需要哪个服务(组件),IoC容器会帮你装配(即负责将它们关联在一起),被动接受装配;
    主动变被动,好莱坞法则:别打电话给我们,我们会打给你;
    迪米特法则(最少知识原则):不知道依赖的具体实现,只知道需要提供某类服务的对象(面向抽象编程),松散耦合,一个对象应当对其他对象有尽可能少的了解,不和陌生人(实现)说话
    IoC是一种让服务消费者不直接依赖于服务提供者的组件设计方式,是一种减少类与类之间依赖的设计原则。

二、DI

  1. DI(Dependency Injection):依赖注入,用一个单独的对象(装配器)来装配对象之间的依赖关系 。

  2. 理解DI问题关键
    谁依赖于谁? ------- 应用程序依赖于IoC容器
    为什么需要依赖? ------- 应用程序依赖于IoC容器装配类之间的关系
    依赖什么东西? ------- 依赖了IoC容器的装配功能
    谁注入于谁? ------- IoC容器注入应用程序
    注入什么东西? ------- 注入应用程序需要的资源(类之间的关系)。

  3. DI优点
    帮你看清组件之间的依赖关系,只需要观察依赖注入的机制(setter/构造器),就可以掌握整个依赖(类与类之间的关系)。
    组件之间的依赖关系由容器在运行期决定,形象的来说,即由容器动态的将某种依赖关系注入到组件之中。
    依赖注入的目标并非为软件系统带来更多的功能,而是为了提升组件重用的概率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不用关心具体的资源来自何处、由谁实现。

4.DI注入方式

  • 构造器注入
    首先,定义一个Person类
public class Person {
    String name;
    int age;
    Person(String name,int age){
        this.name=name;
        this.age=age;
    }
 
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

配置文件,Beans标签下加入如下内容

<?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.xsd">
 
    <bean id="xiaojun" class="main.Person">
        <constructor-arg index="0" value="xiaojun"></constructor-arg>
        <constructor-arg index="1" value="12"></constructor-arg>
    </bean>
    <bean id="xiaohong" class="main.Person">
        <constructor-arg index="0" value="xiaohong"></constructor-arg>
        <constructor-arg index="1" value="11"></constructor-arg>
    </bean>
</beans>

测试代码

import main.Person;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
 
public class PersonTest {
    public static void main(String[] args){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
        Person person = (Person) applicationContext.getBean("xiaojun");
        System.out.println(person.toString());
    }
}

结果如下

Person{name=‘xiaojun’, age=12}
属性注入
在刚才Person类基础上添加set方法(这个是必须的)

public class Person {
    String name;
    int age;
 
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
 
    public void setName(String name) {
        this.name=name;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
}

配置文件如下

<?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.xsd">
 
    <bean id="xiaojun" class="main.Person">
        <property name="name" value="小山"></property>
        <property name="age" value="22"></property>
    </bean>
</beans>

使用上面的测试方法可得如下结果

Person{name=‘小山’, age=22}
接口注入
定义一个Clothes的枚举类

public enum Clothes {
     coat,
    T_shirt,
    skirt,
    jeans,
}

不同的人穿不同的衣服,定义一个穿衣服的接口

public interface WearClothes {
    public void wear(Clothes clothes);
}
穿衣服接口的实现类

public class WearClothesImlp implements WearClothes{
    Clothes clothes;
    @Override
    public void wear(Clothes clothes) {
        this.clothes=clothes;
        System.out.println("People wear "+ clothes);
    }
}

通过示例发现,这个接口注入挺像set注入的,只不过把set改名了。
因为WearClothesImlp类依赖于WearClothes接口,增加了耦合性。所以Spring不支持接口注入。

三、IoC和DI由什么关系呢?

其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

四、为什么会出现IoC和DI ?

通常我们在一个类的方法内使用另一个类方法,这时候就需要在类方法内创建对象,然后使用该方法。这样会增加该类对另一类的耦合性。在编程中我们一般建议高内聚,低耦合。所以,当我们使用IoC和DI后,就会发现问题迎刃而解。
Spring框架正式采用这种方法,我们把创建对象的任务交给IoC容器,由IoC容器代替我们进行创建对象的工作。我们无需关系对象设么时候创建,只需要创建配置文件的容器对象,我们在使用时用getBean方法获取到该对象就可以了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章