深入剖析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 条评论
登录 后参与评论

相关文章

来自专栏日常分享

Spring AOP的一个简单实现

首先配置XML:service采用和之前一样的代码,只是没有通过实现接口来实现,而是直接一个实现类。transactionManager依旧为之前的事务管理器。

721
来自专栏星汉技术

原 荐 Scala的面向对象

----------目录--------------------------------------------------------- 1.Scala简介和...

29513
来自专栏抠抠空间

面向对象基础

1 class Person: #定义一个Person类;类名后面可以加括号,也可以不加。类名第一...

2576
来自专栏java工会

java设计模式-单例模式

1666
来自专栏Python爬虫实战

设计模式:单例模式

想想一下这个场景,一个系统中可以存在多个打印任务,但是只有一个正在工作的任务。我们怎样才能保证一个类只有一个实例并且这个实例易于被访问呢?一个全局变量可以使得一...

632
来自专栏MyBlog

Effective.Java 读书笔记(4)非实例化

有时你想要编写一个类,这个类只是静态方法和静态域的组成,这样的一个类获得一个糟糕的名声因为一些人滥用他们为了避免对对象的术语进行思考,但是他们的确是有用的

622
来自专栏架构师之旅

Spring框架知识总结-注入Bean的各类异常

近日整合sping和hibernate框架时遇到了一系列的异常,本次主要说明一下spring框架可能出现的异常及解决方案。 我们借助sping强...

1868
来自专栏吴伟祥

spring为何要注入接口,而注入接口的实现类就会报错

  首先说明,注入的对象确实为实现类的对象。(并不是实现类的代理对象,注入并不涉及代理)

621
来自专栏栗霖积跬步之旅

java多线程编程核心技术——第六章总结

1.0立即加载/“饿汉式”   立即加载:实用类的时候已经将对象创建完毕,常见的实现方法就是直接new实例化。   注:是在调用方法前,就已经实例化了(通常是...

1746
来自专栏日常分享

Java 多态方法构造器执行方法

可见,当我们试图构造一个B时,应该会优先构造B的父类A,所以会调用父类A的构造函数A(),所以会输出

815

扫码关注云+社区