深入剖析Spring(一)——IoC的基本概念(从面向对象角度介绍)

IoC与DI

IoC和DI是Spring的两个核心概念,很多人都把它们视为相同的东西,但事实并非如此。 IoC(Inversion of Control):控制反转。 DI(Dependency Injection):依赖注入。

为了方便理解,先给出结论:

控制反转是目的,依赖注入是实现控制反转的手段。

控制反转是一种面向对象的思想,它是一种宽泛的概念,只要一个类将对它内部状态的控制权交由其他机制去完成即为『控制反转』。控制反转是为了降低类与类之间的耦合度。

而Spring采用依赖注入这一具体的手段来达到控制反转的目的。

依赖注入详解 一个类内部往往有很多成员变量,如:

class A {
    private Person chaimm;
}

上述代码在面向对象中可以描述为:

  • A类和Person类之间存在依赖关系;
  • A依赖于Person;
  • A为依赖类;
  • Perosn为被依赖类;

通常情况下,依赖类需要自己去创建并维护被依赖类的对象,如:

class A {
    private Person chaimm = new Person();
}

但依赖注入的做法是:将被依赖对象的创建与维护工作交由专门的机构,而依赖类中只需要声明所需要的成员变量。 也就是说,依赖类原本需要主动去获取对象,但采用依赖注入后对象由第三方机构提供,自己仅需声明需要什么对象即可。 这样做的目的就是为了降低两个类之间的耦合程度。 PS:在Spring中,那个创建、管理对象的机构就称为『IoC Service Provider』。

但此时还没体现出依赖注入能降低耦合度这一点,只有当依赖注入与面向接口编程结合起来,才能真正发挥依赖注入的优势。接下来先介绍一下『面向接口编程』。

什么是面向接口编程? 一个类依赖其他类的目的是为了获取其他类所提供的服务,可能这种服务有多种实现,我们可能需要根据不同的场景使用不同的实现。此时,我们可以使用多态,将同一功能的多种实现抽象出一个接口,并为所有实现定义一套相同的API。在使用时声明接口类型的变量而非实现类的变量,并将实现类的对象赋给接口变量,最后用接口变量去调用实现类的服务,如:

class A {
    private Super super = new SuperImpl_1();

    public static void main ( String[] args ) {
        // 使用Super提供的服务
        super.method_1();
        super.method_2();
        super.method_3();
    }
}

这样,当想使用SuperImpl_2提供的功能时,只需替换Super的实现类,其他地方不做任何变化:

private Super super = new SuperImpl_2();

上述过程就是面向接口编程的思想:若某一类服务有多种不同的实现,我们需要抽象出一个接口,并在接口中定义一套API。在使用时声明接口类型变量,并用实现类的对象赋值。接下来通过接口类型的变量调用服务即可。当功能发生变化时,仅需替换实现类即可。

在面向接口编程的基础上使用依赖注入的好处 上述过程如果要换一种实现,就必须要修改A类的代码,再重新编译。而使用了依赖注入后,由于依赖类不需要自己创建维护被依赖对象,该过程由IoC Service Provider完成。因此,当需要替换实现类时,只需在IoC Service Provider中修改,被依赖类、依赖类都不会受到影响,此时这两个类是松耦合的。

依赖注入的三种方式

下面介绍三种方式,将被依赖对象注入给依赖类。

1. 构造器注入

将被依赖对象通过构造函数的参数注入给依赖对象,并且在初始化对象的时候注入。

优点: 对象初始化完成后便可获得可使用的对象。

缺点: 1. 当需要注入的对象很多时,构造器参数列表将会很长; 2. 不够灵活。若有多种注入方式,每种方式只需注入指定几个依赖,那么就需要提供多个重载的构造函数,麻烦。

2. setter方法注入

IoC Service Provider通过调用成员变量提供的setter函数将被依赖对象注入给依赖类。

优点: 灵活。可以选择性地注入需要的对象。

缺点: 依赖对象初始化完成后由于尚未注入被依赖对象,因此还不能使用。

3. 接口注入

依赖类必须要实现指定的接口,然后实现该接口中的一个函数,该函数就是用于依赖注入。该函数的参数就是要注入的对象。 接口注入中,接口的名字、函数的名字都不重要,只要保证函数的参数是要注入的对象类型即可。

缺点: 侵入行太强,不建议使用。

PS:什么是侵入行? 如果类A要使用别人提供的一个功能,若为了使用这功能,需要在自己的类中增加额外的代码,这就是侵入性。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏云霄雨霁

内存可见性

1412
来自专栏salesforce零基础学习

salesforce 零基础开发入门学习(十五)salesforce中formula的使用(不含Date/Time)

本文参考官方的formula介绍PDF:https://resources.docs.salesforce.com/200/latest/en-us/sfdc/...

2005
来自专栏Pythonista

redis学习

集合相关的操作也很丰富,如添加新元素、删除已有元素、取交集、取并集、取差集等。我们来看例子:

1604
来自专栏java一日一条

从根源上解析 Java volatile 关键字的实现

也就是说,如果一个变量在多个CPU中都存在缓存(一般在多线程编程时才会出现),那么就可能存在缓存不一致的问题。

531
来自专栏Java 源码分析

synchronized 原理分析

synchronized 原理分析 1. 在阅读源码时做了大量的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限,并且代码阅读起来没有 IDE 方...

2303
来自专栏老马说编程

(65) 线程的基本概念 / 计算机程序的思维逻辑

在之前的章节中,我们都是假设程序中只有一条执行流,程序从main方法的第一条语句逐条执行直到结束。从本节开始,我们讨论并发,在程序中创建线程来启动多条执行流,并...

2107
来自专栏奔跑的蛙牛技术博客

synchronized 详解

那到底使用synchronized关键字是不是就是一个监视器? 不懂,理解的话可以向我发邮件gaomengjie_1@163.com,我虚心求教

573
来自专栏王亚昌的专栏

【C协程】ucontext入解

In  a  System V-like environment, one has the type ucontext_t defined in <uconte...

952
来自专栏架构师之旅

Java并发编程:volatile关键字解析

volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Jav...

471
来自专栏GreenLeaves

JS模块加载系统设计V1

一、require模块 +function() { var path = location.protocol + "//" + loca...

1895

扫码关注云+社区