【JavaEE】深入理解Spring IoC与DI:从传统开发到依赖注入的转变


IoC & DI ⼊⻔

IoC:Inversion of Control (控制反转)

DI:Dependency Injection

在前⾯我们学习了Spring Boot和Spring MVC的开发, 可以完成⼀些基本功能的开发了, 但是什么是Spring呢? Spring, Spring Boot 和SpringMVC⼜有什么关系呢?

我们先看什么是Spring

什么是Spring

通过前⾯的学习, 我们知道了Spring是⼀个开源框架, 他让我们的开发更加简单. 他⽀持⼴泛的应⽤场景, 有着活跃⽽庞⼤的社区, 这也是Spring能够⻓久不衰的原因.

但是这个概念相对来说, 还是⽐较抽象.

Spring两大核心思想:

  1. IoC
  2. AOP

我们⽤⼀句更具体的话来概括Spring, 那就是: Spring 是包含了众多⼯具⽅法的 IoC 容器

什么是容器?什么是 IoC 容器?接下来我们⼀起来看

什么是容器

容器是⽤来容纳某种物品的(基本)装置。

来⾃:百度百科

⽣活中的⽔杯, 垃圾桶, 冰箱等等这些都是容器.

我们想想,之前我们接触的容器有哪些?

  • List/Map -> 数据存储容器
  • Tomcat -> Web 容器

Spring容器,装的是 IoC 对象

什么是IoC

IoC 是Spring的核⼼思想, 也是常⻅的⾯试题, 那什么是IoC呢?

其实IoC我们在前⾯已经使⽤了, 我们在前⾯讲到, 在类上⾯添加 @RestController@Controller 注解, 就是把这个对象交给Spring管理, Spring 框架启动时就会加载该类. 把对象交给Spring管理, 就是IoC思想.

IoC: Inversion of Control (控制反转), 也就是说 Spring 是⼀个"控制反转"的容器.

什么是控制反转呢? 也就是控制权反转. 什么的控制权发⽣了反转? 获得依赖对象的过程被反转了,交给了Spring

也就是说, 当需要某个对象时, 传统开发模式中需要⾃⼰通过 new 创建对象, 现在不需要再进⾏创建, 把创建对象的任务交给容器, 程序中只需要依赖注⼊ (Dependency Injection,DI) 就可以了.

这个容器称为:IoC容器. Spring是⼀个IoC容器, 所以有时Spring 也称为Spring 容器

控制反转是⼀种思想, 在⽣活中也是处处体现.

⽐如⾃动驾驶, 传统驾驶⽅式, ⻋辆的横向和纵向驾驶 控制权由驾驶员来控制, 现在交给了驾驶⾃动化系统来控制, 这也是控制反转思想在⽣活中的实现.

⽐如招聘, 企业的员⼯招聘,⼊职, 解雇等控制权, 由⽼板转交给给HR(⼈⼒资源)来处理

IoC介绍

接下来我们通过案例来了解⼀下什么是IoC

需求: 造⼀辆⻋

传统程序开发

我们的实现思路是这样的:

先设计轮⼦(Tire),然后根据轮⼦的⼤⼩设计底盘(Bottom),接着根据底盘设计⻋⾝(Framework),最后根据⻋⾝设计好整个汽⻋(Car)。这⾥就出现了⼀个"依赖"关系:汽⻋依赖⻋⾝,⻋⾝依赖底盘,底盘依赖轮⼦.

在这里插入图片描述

最终程序的实现代码如下:

//Main.java
public class Main {
    public static void main(String[] args) {
        Car car=new Car();
        car.run();
    }
}
//Car.java
public class Car {
    private FrameWork frameWork;

    public Car() {
        frameWork=new FrameWork();
        System.out.println("car init...");
    }

    public void run(){
        System.out.println("car run...");
    }
}
//FrameWork.java
public class FrameWork {
    private  Bottom bottom;

    public FrameWork() {
        bottom=new Bottom();
        System.out.println("frameWork init...");
    }
}
//Bottom.java
public class Bottom {
    private Tire tire;

    public Bottom(){
        tire=new Tire();
        System.out.println("bottom init...");
    }
}
//Tire.java
public class Tire {
    private int size=17;

    public Tire(){
        System.out.println("tire init...");
    }
}
问题分析

这样的设计看起来没问题,但是可维护性却很低.

接下来需求有了变更: 随着对的⻋的需求量越来越⼤, 个性化需求也会越来越多,我们需要加⼯多种尺⼨的轮胎.

那这个时候就要对上⾯的程序进⾏修改了,修改后的代码如下所⽰:

在这里插入图片描述

修改之后, 其他调⽤程序也会报错, 我们需要继续修改

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

完整代码如下:

public class Main {
    public static void main(String[] args) {
        Car car=new Car(20);
        car.run();

        Car car2=new Car(21);
        car2.run();
    }
}

public class Car {
    private FrameWork frameWork;

    public Car(int size) {
        frameWork=new FrameWork(size);
        System.out.println("car init...");
    }

    public void run(){
        System.out.println("car run...");
    }
}

public class FrameWork {
    private  Bottom bottom;

    public FrameWork(int size) {
        bottom=new Bottom(size);
        System.out.println("frameWork init...");
    }
}

public class Bottom {
    private Tire tire;

    public Bottom(int size){
        tire=new Tire(size);
        System.out.println("bottom init...");
    }
}

public class Tire {
    private int size;

    public Tire(int size){
        this.size=size;
        System.out.println("tire init...size:"+size);
    }
}

从以上代码可以看出,以上程序的问题是:当最底层代码改动之后,整个调⽤链上的所有代码都需要修改.

程序的耦合度⾮常⾼(修改⼀处代码, 影响其他处的代码修改)

即一辆车需要知道车身是怎么样的,车身需要知道底盘是怎么样的,底盘需要知道轮子是怎么样的,耦合度就很高

解决方案

在上⾯的程序中, 我们是根据轮⼦的尺⼨设计的底盘,轮⼦的尺⼨⼀改,底盘的设计就得修改. 同样因为我们是根据底盘设计的⻋⾝,那么⻋⾝也得改,同理汽⻋设计也得改, 也就是整个设计⼏乎都得改

我们尝试换⼀种思路, 我们先设计汽⻋的⼤概样⼦,然后根据汽⻋的样⼦来设计⻋⾝,根据⻋⾝来设计底盘,最后根据底盘来设计轮⼦. 这时候,依赖关系就倒置过来了:轮⼦依赖底盘, 底盘依赖⻋⾝,⻋⾝依赖汽⻋

这就类似我们打造⼀辆完整的汽⻋, 如果所有的配件都是⾃⼰造,那么当客⼾需求发⽣改变的时候,⽐如轮胎的尺⼨不再是原来的尺⼨了,那我们要⾃⼰动⼿来改了,但如果我们是把轮胎外包出去,那么即使是轮胎的尺⼨发⽣改变了,我们只需要向代理⼯⼚下订单就⾏了,我们⾃⾝是不需要出⼒的.

在这里插入图片描述

如何来实现呢:

我们可以尝试不在每个类中⾃⼰创建下级类,如果⾃⼰创建下级类就会出现当下级类发⽣改变操作,⾃⼰也要跟着修改.

此时,我们只需要将原来由⾃⼰创建的下级类,改为传递的⽅式(也就是注⼊的⽅式),因为我们不需要在当前类中创建下级类了,所以下级类即使发⽣变化(创建或减少参数),当前类本⾝也⽆需修改任何代码,这样就完成了程序的解耦.

IoC程序开发

基于以上思路,我们把调⽤汽⻋的程序⽰例改造⼀下,把创建⼦类的⽅式,改为注⼊传递的⽅式.

具体实现代码如下:

public class Main {
    public static void main(String[] args) {
        Tire tire=new Tire(17,"red");
        Bottom bottom=new Bottom(tire);
        FrameWork frameWork=new FrameWork(bottom);
        Car car=new Car(frameWork);
        car.run();
    }
}

public class Car {
    private FrameWork frameWork;

    public Car(FrameWork frameWork) {
        this.frameWork = frameWork;
        System.out.println("car init...");
    }
    public void run(){
        System.out.println("car run...");
    }
}

public class FrameWork {
    private Bottom bottom;

    public FrameWork(Bottom bottom) {
        this.bottom = bottom;
        System.out.println("frameWork init...");
    }
}

public class Bottom {
    private Tire tire;

    public Bottom(Tire tire) {
        this.tire = tire;
        System.out.println("bottom init...");
    }
}

public class Tire {
    private int size;
    private String color;

    public Tire(int size,String color) {
        this.size = size;
        this.color=color;
        System.out.println("tire init...size:"+size+",color:"+color);
    }
}

代码经过以上调整,⽆论底层类如何变化,整个调⽤链是不⽤做任何改变的,这样就完成了代码之间的解耦,从⽽实现了更加灵活、通⽤的程序设计了。

汽车只需要知道它需要一个车身,车身只需要知道它需要一个底盘,底盘只需要知道它需要轮子,不需要知道具体细节,就解耦合了

传统开发方法:自己做饭

想象一下,你每天晚上都要自己做饭吃。这个过程就类似于传统开发方法:

  1. 自行准备:你需要自己购买食材(类似于创建对象),准备调料,做每一道菜。
  2. 高耦合:每个步骤都依赖前一个步骤的完成,如果你忘了买某样食材,整个晚餐都可能无法完成。
  3. 灵活性差:如果突然有朋友来访,而你准备的食材和菜谱是固定的,很难快速适应增加的人数或特殊的饮食需求。
  4. 难以更换:一旦你决定做某个菜,就必须按照原计划进行,中途改变计划将会非常麻烦。

IoC(依赖注入)方法:外卖服务

现在,想象你改为使用一家提供全套餐服务的外卖(这就像是依赖注入):

  1. 服务提供:你只需要选择想吃什么,外卖服务提供完整的餐点,你不需要自己准备食材或调料。
  2. 解耦:你不依赖于具体的购物或烹饪过程,只需等待成品送达。
  3. 灵活性高:突然有更多人需要吃饭?只需增加订单数量或更改订单即可,无需自己重新准备。
  4. 易于更换:想改变菜单?只需取消当前订单,重新下一个新的订单即可。
IoC优势

在传统的代码中对象创建顺序是:Car -> Framework -> Bottom -> Tire

改进之后解耦的代码的对象创建顺序是:Tire -> Bottom -> Framework -> Car

在这里插入图片描述

我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了Framework,Framework 创建并创建了 Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由当前类控制了.

这样的话, 即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想

到这⾥, 我们⼤概就知道了什么是控制反转了, 那什么是控制反转容器呢?也就是IoC容器

这部分代码, 就是IoC容器做的⼯作.

从上⾯也可以看出来, IoC容器具备以下优点:

资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集中管理,实现资源的可配置和易管理。第⼆,降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合度。

  1. 资源集中管理: IoC容器会帮我们管理⼀些资源(对象等), 我们需要使⽤时, 只需要从IoC容器中去取就可以了

  2. 我们在创建实例的时候不需要了解其中的细节, 降低了使⽤资源双⽅的依赖程度, 也就是耦合度.

Spring 就是⼀种IoC容器, 帮助我们来做了这些资源管理.

Spring帮我们管理对象,我们要做的:

  1. 告诉Spring帮我们管理哪些对象(
  2. 知道如何取出这些对象(
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值