还不理解?浅谈SpringIOC!

471 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

欲持一瓢酒,远慰风雨夕

到底什么是IOC

当你点进来,相信你已经看到过无数篇关于IOC的文章了,IOC嘛,字面意思,Inversion of Control,控制反转,意思是把创建对象的权利从我们的手中,移交到SpringIOC容器中,实现控制权的反转(从开发者到IOC容器)。这段话说实话你只要看过一点点八股文都能做到倒背如流。

其实呢,IOC是一个思想,而不是一个具体的实现,它的具体实现我们可以认为是DI,后面会说到。

为了搞清楚SpringIOC到底是什么,我们来看看没有IOC容器我们是怎么做的。

首先我需要建造一个大别墅,我需要门和窗户,在无参构造函数的情况下,也就是我没有指定用什么样的门,什么样的窗户的情况下,我会new出两个默认的门和窗户对象,也就是说我需要自行创建对象,管理对象。

public class House {
    private final Door door;
    private final Window window;
​
    public House() {
        this.door = new Door();
        this.window = new Window();
    }
​
    public House(Door door, Window window) {
        this.door = door;
        this.window = window;
    }
​
    public void buildHouse() {
        System.out.println("house build successfully!");
    }
}

那如果是我使用IOC容器的情况下呢?我们只需要注入两个对象,表示当前House对象依赖于Door和Window对象,至于这两个对象是怎么来的,我并不需要关心,这也就是我们常说的DI(依赖注入)。

所以,SpringIOC只是实现了对于对象创建的控制权的转换,而对于对象之间的依赖,也依然需要我们开发者来管理。

@Getter
@Setter
public class House {
    @Autowired
    private Door door;
    @Autowired
    private Window window;
​
    public void buildHouse() {
        System.out.println("house build successfully!");
    }
}

从上面的代码可以看到,从没有IOC到使用IOC容器,耦合依然存在,一旦我们的内部成员,比如door发生了变化,不论是哪一种方式,我们依然需要去修改代码。我们可以简单看一下上面对象创建的流程。

  1. 创建一个House对象
  2. IOC容器发现其中依赖了两个对象
  3. 先去创建Door对象,完成创建后,放入IOC容器
  4. 再去创建Window对象,完成创建后,放入IOC容器
  5. 直接将创建好的Door和Window对象从容器中取出,用作House对象的私有成员
  6. 完成House对象的创建

什么是DI,依赖注入

从下面Martin Fowler的文章中我们可以看到,所谓的DI,就是IOC的不同类型的实现,分别是接口注入,setter注入,构造函数注入,这三种就是我们常说的IOC设计理念的三种实现类型。

image.png

大家可以看到我上面是通过注解的方式来注入的,换汤不换药,本质上也是这三种方式的其中一种。

也就是说,我们使用对象的过程中,对象之间的依赖关系,早在我们设计出当前的对象的时候就已经存在了。

只不过IOC使得我们在设计对象的时候,不需要去关系我当前对象所依赖的对象是如何创建出来的,例如我设计House对象时,不需要去关心Door和Window是怎么创建出来的,我只需要知道的是,我当前设计的House对象需要依赖于Door和Window,此过程就是我们说的DI——依赖注入,我们把对象间的依赖关系,在编码的同时,注入到了当前对象,也就是IOC容器在创建我当前对象时,才能知道此对象的依赖注入关系,那最后创建对象的操作就交给SpringIOC容器去处理了。

IOC(控制反转)的好处

  • 松耦合:比起解耦,我更愿意称为松耦合,对象之间的耦合关系依然存在,只不过是多了一个IOC容器来解耦,但对象与IOC容器之间的耦合依然存在。
  • 单例:IOC容器本身是单例的,我们使用的对象也是单例的,也可以享受到单例的好处,效率很高,不浪费空间。
  • 隐藏对象创建细节:这一点我们可以把IOC看作是一个工厂,我们不需要关心对象是如何创建的,只管拿来用,提高了开发效率。
  • 便于AOP操作:代理可以直接去拿IOC容器中的对象,对于使用者是透明的。

至于很多人说的统一配置达到解耦的操作,我个人认为IOC容器是没办法达到这种效果的,因为当我们A对象注入了B接口,如果B接口多了或者更换了一个实现,我们也需要去修改A对象中的代码,这在我看在并不算是完全解耦了。

所以IOC容器带来的更重要的优点是,失去对象创建对象的权利的同时,获得好处(不用再考虑对象的创建)。

后记

对于IOC我相信大家都非常熟悉了,IOC容器最主要的解耦作用相信大家都能答得出来。

那如果问你IOC怎么做到解耦的呢?

关于IOC就先说这么多啦,有什么问题欢迎大家评论区讨论哦。