控制反转 vs 依赖注入

一、介绍

ioc(控制反转)意味对象不用自己创建需要的对象,相反,它们直接从其他地方获得需要的对象。DI(依赖注入)是一个IoC的具体实现,即在运行时通过不同的注入技术,如setter注入、constructor注入或接口注入来提供对象依赖。由于DI是IoC的具体实现,因此看起来两则貌似一致,但是它们的侧重点不同,IoC侧重对象的创建过程,即对象创建创建的控制权交给了其他地方,而DI侧重依赖的注入方式,即通过不同的技术动态注入依赖。

在spring中,ioc和DI经常会混为一谈,因为,spring文档自己都说Ioc也被称为DI,但是在spring中,Ioc更多的是用来使能DI的。

二、控制反转

ioc可以这么理解,其他人来帮你创建对象,而不是自己手动编码“new MyObject”。这个其他人一般就是IOC容器,ioc容器来帮你创建对象,而ioc容器不只是创建对象,还会管理对象的声明周期,每个不同的生命周期都会被ioc容器调用不同的方法。一些ioc容器,比如spring容器、java Servlets容器和Akka Actors。spring容器不仅负责bean的创建,还控制bean的生命周期。servlets容器也是负责创建servlet和管理他的生命周期

尽管通过IOC,让出了对象创建的控制权,但是还是需要提供模板来让容器创建对象,如下面所示:

IoC Container Managed Objects Name Managed Objects Definition
Spring Container Bean Classes defined with annotations/XML configuration
Servlet Container Servlet Classes implementing interface Servlet
Actor System Actor Classes extending trait Actor

三、依赖注入

简单来说,DI和硬编码注入是相对的:

//Hardcoded dependency
public class MyClass { 
    private MyDependency myDependency = new MyDependency(); 
}
//Injected dependency
public class MyClass { 
    private MyDependency myDependency;
    
    public MyClass(MyDependency myDependency){
        this.myDependency = myDependency;
    }
}

可以看出,可以通过传参给构造函数、setter方法来注入依赖。但是DI有个缺点,就是需要管理依赖和手动注入依赖,即创建依赖对象,然后传参。

下面看一个例子,MyClass1依赖MyClass2,MyClass2依赖MyClass3:

public class MyClass3 {
    public void doSomething(){}
}

//MyClass2 depends on MyClass3
public class MyClass2 {
    private MyClass3 myClass3;

    public MyClass2(MyClass3 myClass3){
        this.myClass3 = myClass3;
    }

    public void doSomething(){
        myClass3.doSomething();
    }
}

//MyClass1 depends on MyClass2
public class MyClass1 {
    private MyClass2 myClass2;

    public MyClass1(MyClass2 myClass2){
        this.myClass2 = myClass2;
    }

    public void doSomething(){
        myClass2.doSomething();
    }
}

public class Main {

    public static void main(String[] args) {

        //All dependencies need to be managed by the developer
        MyClass3 myClass3 = new MyClass3();
        MyClass2 myClass2 = new MyClass2(myClass3);
        MyClass1 myClass1 = new MyClass1(myClass2);

        myClass1.doSomething();
    }
}

继续下去,如果MyClass2还依赖MyClass4,那么还需要手动添加新依赖:

public class MyClass2 {
    private MyClass3 myClass3;
    private MyClass4 myClass4;

    public MyClass2(MyClass3 myClass3, MyClass4 myClass4){
        this.myClass3 = myClass3;
        this.myClass4 = myClass4;
    }

    public void doSomething(){
        myClass3.doSomething();
        myClass4.doSomething();
    }
}

public class Main {

    public static void main(String[] args) {

        MyClass4 myClass4 = new MyClass4();
        MyClass3 myClass3 = new MyClass3();
        MyClass2 myClass2 = new MyClass2(myClass3, myClass4);
        MyClass1 myClass1 = new MyClass1(myClass2);

        myClass1.doSomething();
    }
}

即使这样描述并不是错的,但是现实中的一个程序成百上千个依赖分散在不同的代码区域中,我们需要找到,然后再集中起来创建依赖和管理依赖,如上面的例子似的。很不方便,这就是接下来要讨论的。

四、Ioc和DI配合

依赖太多,依赖的创建和依赖注入都很复杂。但是通过IOC,这些依赖可以被IOC容器来管理、创建。通过使用类似@Autowired的注解,容器可以自动在需要的地方注入依赖,因此程序员不需要关注依赖的创建和注入。

public class MyClass1 {
    
    @Autowired
    private MyClass2 myClass2;

    public void doSomething(){
        myClass2.doSomething();
    }
}
public class MyClass2 {

    @Autowired
    private MyClass3 myClass3;
    @Autowired
    private MyClass4 myClass4;

    public void doSomething(){
        myClass3.doSomething();
        myClass4.doSomething();
    }
}

上面没有了对象的创建过程,这是因为ioc容器被提供模板后,会自动创建依赖,比如spring用<bean>标签定义模板。

扫描二维码关注公众号,回复: 3284506 查看本文章

参考

http://blog.bytecode.tech/inversion-of-control-vs-dependency-injection/

猜你喜欢

转载自blog.csdn.net/jdbdh/article/details/82800306