【SpringBoot】从零开始全面解析Spring Ioc&DI (一)

本篇博客给大家带来的是SpringBoot的知识点, 本篇介绍Spring IoC 和 DI 相关知识.
🐎文章专栏: JavaEE进阶
🚀若有问题 评论区见
❤ 欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .

王子,公主请阅🚀

  • 要开心
  • [1. loC & DI 入门](#1. loC & DI 入门)
    • [1.1 Spring 是什么?](#1.1 Spring 是什么?)
      • [1.1.1 什么是容器?](#1.1.1 什么是容器?)
      • [1.1.2 什么是 IoC?](#1.1.2 什么是 IoC?)
    • [1.2 IoC 具体介绍](#1.2 IoC 具体介绍)
      • [1.2.1 传统程序开发](#1.2.1 传统程序开发)
      • [1.2.2 问题分析](#1.2.2 问题分析)
      • [1.2.3 解决方案](#1.2.3 解决方案)
      • [1.2.4 IoC 优势](#1.2.4 IoC 优势)
    • [1.3 DI 介绍](#1.3 DI 介绍)
  • [2. loC & DI 使用](#2. loC & DI 使用)

要开心

要快乐

顺便进步

1. loC & DI 入门

1.1 Spring 是什么?

通过前面的学习, 我们知道了Spring是一个开源框架, 他让我们的开发更加简单. 他支持广泛的应用场景, 有着活跃而庞大的社区, 这也是Spring能够长久不衰的原因.
但是这个概念相对来说, 还是比较抽象.
我们用一句更具体的话来概括Spring, 那就是: Spring 是包含了众多工具方法的 IoC 容器 。
那问题来了,什么是 IoC 容器?接下来我们一起来看 .

1.1.1 什么是容器?

容器是用来容纳某种物品的(基本)装置。⸺来自:百度百科
生活中的水杯, 垃圾桶, 冰箱等等这些都是容器.
之前接触的容器有哪些?
List/Map -> 数据存储容器
Tomcat -> Web 容器

1.1.2 什么是 IoC?

这个问题也是常见的面试题。
IoC 是Spring的核心思想. 其实我们在上篇文章就已经使用过 IoC思想了.

在类上面添加 @RestController 和
@Controller 注解, 就是把这个对象交给Spring管理, Spring 框架启动时就会加载该类. 把对象交给Spring管理, 就是IoC思想。

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

控制反转就是 控制权反转. 获得依赖对象的过程被反转了。
也就是说, 当需要某个对象时, 传统开发模式中需要自己通过 new 创建对象, 现在不需要再进行创建, 把创建对象的任务交给容器, 程序中只需要依赖注入 (Dependency Injection,DI)就可以了.
这个容器称为:IoC容器. Spring是一个IoC容器, 所以有时Spring 也称为Spring 容器.

1.2 IoC 具体介绍

通过案例来了解以下什么是 IoC.
需求: 造一辆车.

1.2.1 传统程序开发

传统实现思路是这样的:
先设计轮子(Tire),然后根据轮子的大小设计底盘(Bottom),接着根据底盘设计车身(Framework),最后根据车身设计好整个汽车(Car)。这里就出现了一个"依赖"关系:汽车依赖车身,车身依赖底盘,底盘依赖轮子.

最终程序实现代码如下:

java 复制代码
1 public class NewCarExample {
2 public static void main(String[] args) {
3 Car car = new Car();
4 car.run();
5 }
6
7 /**
8 * 汽⻋对象
9 */
10 public class Car {
11 private Framework framework;
12
13 public Car() {
14 framework = new Framework();
15 System.out.println("Car init....");
16 }
17 public void run(){
18 System.out.println("Car run...");
19 }
20 }
21
22 /**
23 * ⻋⾝类
24 */
25 public class Framework {
26 private Bottom bottom;
27
28 public Framework() {
29 bottom = new Bottom();
30 System.out.println("Framework init...");
31 }
32 }
33
34 /**
35 * 底盘类
36 */
37 public  class Bottom {
38 private Tire tire;
39
40 public Bottom() {
41 this.tire = new Tire();
42 System.out.println("Bottom init...");
43 }
44 }
45
46 /**
47 * 轮胎类
48 */
49 public class Tire {
50 // 尺⼨
51 private int size;
52
53 public Tire(){
54 this.size = 17;
55 System.out.println("轮胎尺⼨:" + size);
56 }
57 }
58 }

1.2.2 问题分析

这样的设计看起来没问题,但是可维护性却很低.
如果需求有了变更: 随着对的车的需求量越来越大, 个性化需求也会越来越多,我们需要加工多种尺寸的轮胎。

java 复制代码
public class Tire {
    private int size;

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

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

java 复制代码
public class Bottom {
    private Tire tire;

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

public class Framework {
    private Bottom bottom;

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

public class Car {
    private Framework framework;
    public Car(int size) {
        framework = new Framework(size);
        this.framework = framework;
        System.out.println("framework init...");
    }

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

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

1.2.3 解决方案

如果自己创建下级类就会出现当下级类发生改变,自己也要跟着修改. 因此我们可以尝试不在每个类中创建下级类。
只需要将原来由自己创建的下级类,改为传递的方式(也就是注入的方式),因为我们不需要在当前类中创建下级类了,所以下级类即使发生变化(创建或减少参数),当前类本身也无需修改任何代码,这样就完成了程序的解耦.

java 复制代码
public class Main {
    public static void main(String[] args) {
//        Car car = new Car();
//        car.run();
        Tire tire = new Tire(15);
        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("framework 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("bottom init...");
    }
}

public class Bottom {
    private Tire tire;

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

public class Tire {
    private int size;

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

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

1.2.4 IoC 优势

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

通用程序的实现代码,类的创建顺序是反的,传统代码是 Car 控制并创建了 Framework,Framework 控制并创建了 Bottom,依次往下,而改进之后的控制权发生的反转,不再是使用方对象创建并控制依赖对象了,而是把依赖对象注入将当前对象中,依赖对象的控制权不再由当前类控制了.
这样的话, 即使依赖类发生任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是 IoC 的实现思想。

从上面也可以看出来, IoC容器具备以下优点:
① 资源不由使用资源的双方管理,而由不使用资源的第三方管理,资源集中管理, 实现资源的可配置和易管理。

② 降低了使用资源双方的依赖程度,也就是耦合度。

1.3 DI 介绍

DI: Dependency Injection(依赖注入)

容器在运行期间, 动态的为应用程序提供运行时所依赖的资源,称之为依赖注入。

当程序运行时需要某个资源,此时容器就为其提供这个资源.
从这点来看, 依赖注入(DI)和控制反转(IoC)是从不同的角度的描述的同一件事情,就是指通过引 入 IoC 容器,利用依赖关系注入的方式,实现对象之间的解耦。

上述代码中, 是通过构造函数的方式, 把依赖对象注入到需要使用的对象中的。

IoC 是一种思想,也是"目标", 而思想只是一种指导原则,最终还是要有可行的落地方案,而 DI 就属于具体的实现。所以也可以说, DI 是IoC的一种实现。

2. loC & DI 使用

既然 Spring 是一个 IoC(控制反转)容器,作为容器, 那么它就具备两个最基础的功能:
• 存
• 取

Spring 容器 管理的主要是对象, 这些对象, 我们称为"Bean". 我们把这些对象交由Spring管理, 由Spring来负责对象的创建和销毁. 我们程序只需要告诉Spring, 哪些需要存, 以及如何从Spring中取出对象。

打开上篇文章spring-book项目, 在service目录下创建BookService文件, 把BookDao, BookService 交给Spring管理, 完成Controller层, Service层, Dao层的解耦。

  1. Service层及Dao层的实现类,交给Spring管理: 使用注解: @Component
  2. 在Controller层 和Service层 注入运行时依赖的对象: 使用注解 @Autowired

把BookDao 交给Spring管理, 由Spring来管理对象

java 复制代码
@Component
public class BookDao {
    public List<BookInfo> mockData() {
        //理论上该方法应该从数据库获取, 此处先 mock数据.
        List<BookInfo> bookInfos = new ArrayList<>();
        //mock(模拟) 数据
        for (int i = 1; i <= 15; i++) {
            BookInfo bookInfo = new BookInfo();
            bookInfo.setId(i);
            bookInfo.setBookName("图书"+i);
            bookInfo.setAuthor("作者"+i);
            bookInfo.setNum(i*2+1);
            bookInfo.setPrice(new BigDecimal(i*3));
            bookInfo.setPublishName("出版社"+i);
            if(i % 5 == 0) {
                bookInfo.setStatus(2);
//                bookInfo.setStatusCN("不可借阅");
            }else {
                bookInfo.setStatus(1);
//                bookInfo.setStatusCN("可借阅");
            }
            bookInfos.add(bookInfo);
        }
        return bookInfos;
    }
}

把BookService 交给Spring管理, 由Spring来管理对象

java 复制代码
@Service
public class BookService {
    public List<BookInfo> getBookList() {
        BookDao bookDao = new BookDao();
        List<BookInfo> bookInfos = bookDao.mockData();
        for(BookInfo bookInfo : bookInfos) {
            if(bookInfo.getStatus() == 2) {
                bookInfo.setStatusCN("不可借阅");
            }else {
                bookInfo.setStatusCN("可借阅");
            }
        }
        return bookInfos;
    }
}

删除创建BookDao的代码, 从Spring中获取对象

java 复制代码
@Component
public class BookService {
    @Autowired
    private BookDao bookDao;
    public List<BookInfo> getBookList() {
//        BookDao bookDao = new BookDao();
        List<BookInfo> bookInfos = bookDao.mockData();
        for(BookInfo bookInfo : bookInfos) {
            if(bookInfo.getStatus() == 2) {
                bookInfo.setStatusCN("不可借阅");
            }else {
                bookInfo.setStatusCN("可借阅");
            }
        }
        return bookInfos;
    }
}

删除创建BookService的代码, 从Spring中获取对象

java 复制代码
@RequestMapping("/book")
@RestController
public class BookController {
//    @RequestMapping("/getBookList")
//    public List<BookInfo> getBookList() {
//        List<BookInfo> bookInfos = new ArrayList<>();
//        //mock(模拟) 数据
//        for (int i = 1; i <= 15; i++) {
//            BookInfo bookInfo = new BookInfo();
//            bookInfo.setId(i);
//            bookInfo.setBookName("图书"+i);
//            bookInfo.setAuthor("作者"+i);
//            bookInfo.setNum(i*2+1);
//            bookInfo.setPrice(new BigDecimal(i*3));
//            bookInfo.setPublishName("出版社"+i);
//            if(i % 5 == 0) {
//                bookInfo.setStatus(2);
//                bookInfo.setStatusCN("不可借阅");
//            }else {
//                bookInfo.setStatus(1);
//                bookInfo.setStatusCN("可借阅");
//            }
//            bookInfos.add(bookInfo);
//        }
//        return bookInfos;
//    }
    @Autowired
    private BookService bookService;
    @RequestMapping("/getBookList")
    public List<BookInfo> getBookList() {
//        BookService bookService = new BookService();
        return bookService.getBookList();
    }
}

本篇博客到这里就结束啦, 感谢观看 ❤❤❤
🐎期待与你的下一次相遇😊😊😊

相关推荐
c无序6 分钟前
【Go-4】函数
开发语言·后端·golang
Auc2413 分钟前
Neo4j入门第二期(Spring Data Neo4j的使用)
java·spring·neo4j
caihuayuan437 分钟前
Java设计模式: 工厂模式与策略模式
java·大数据·sql·spring·课程设计
ikun·1 小时前
初识 Flask 框架
后端·python·flask
代码的余温2 小时前
ActiveMQ多消费者负载均衡优化指南
java·后端·负载均衡·activemq
Uranus^2 小时前
Spring Boot集成Resilience4j实现微服务容错机制
spring boot·微服务·断路器·resilience4j·容错机制
悟能不能悟4 小时前
Logback 在 Spring Boot 中的详细配置
java·spring boot·logback
奋斗的袍子0074 小时前
Spring Boot 拦截器:解锁5大实用场景
java·spring boot·后端·拦截器·interceptor