一、初识IoC和DI
1.1Spring是什么?
我们知道了Spring是⼀个开源框架,他让我们的开发更加简单.他⽀持⼴泛的应⽤场 景,有着活跃⽽庞⼤的社区,这也是Spring能够⻓久不衰的原因. 但是这个概念相对来说,还是⽐较抽象. 我们⽤⼀句更具体的话来概括Spring,那就是:Spring是包含了众多⼯具⽅法的IoC容器。
1.1.1 容器
容器是⽤来容纳某种物品的(基本)装置。⸺来⾃:百度百科 ⽣活中的⽔杯,垃圾桶,冰箱等等这些都是容器.
在比如:
List/Map->数据存储容器
Tomcat->Web容器
1.1.2 IoC是什么
在类上⾯添加 @RestController 和 @Controller 注解,就是把这个对象交给Spring管理,Spring框架启动时就会加载该类.把对象交 给Spring管理,就是IoC思想
IoC: Inversion of Control (控制反转),也就是说Spring是⼀个"控制反转"的容器
控制反转是什么?-----> 当需要某个对象时,传统开发模式中需要⾃⼰通过new创建对象,现在不需要再进⾏创建, 把创建对象的任务交给容器, 程序中只需要依赖注⼊(DependencyInjection,DI)就可以了. 这个容器称为:IoC容器. Spring是⼀个IoC容器,所以有时Spring也称为Spring容器.
举个生活中的例子:
想象你每天早上都需要一杯咖啡。通常情况下,你自己去厨房找咖啡机和咖啡豆来做咖啡。但在控制反转的情况下,你的家人已经知道你喜欢喝咖啡,并且在你起床前就把泡好的咖啡放在桌上等你。这里,你不再需要自己去找资源(咖啡机和咖啡豆),而是这些资源被“注入”给了你。这就像是依赖注入,外部环境(你的家人)负责准备并提供你需要的东西这种方式让早晨的流程变得更简单、直接,也更灵活,因为你不需要关心咖啡的准备工作了。这就是控制反转的基本思想:将对象的创建和管理从对象自身转移到外部环境。
1.2 IoC的介绍
1.2.1 传统的程序开发
需求:造一辆车
先设计轮⼦(Tire),然后根据轮⼦的⼤⼩设计底盘(Bottom),接着根据底盘设计⻋⾝(Framework),最 后根据⻋⾝设计好整个汽⻋(Car)。这⾥就出现了⼀个"依赖"关系:汽⻋依赖⻋⾝,⻋⾝依赖底盘,底 盘依赖轮⼦.
代码实现:
java">import org.w3c.dom.ls.LSOutput;//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {public static void main(String[] args) {Car car = new Car();}
}//汽车类
class Car {private Tramework tramework;public Car() {tramework = new Tramework();System.out.println("Car init...");}
}//车身类
class Tramework {private Bottom bottom;public Tramework() {bottom = new Bottom();System.out.println("Tramework init...");}
}//地盘类
class Bottom {private Tire tire;public Bottom() {tire = new Tire();System.out.println("Bottom init...");}
}//轮胎类
class Tire {private int size;public Tire() {this.size = 17;System.out.println("Tire size : " + this.size);}
}
当我们要去添加轮胎类的Tire构造方法的形参编译时会出现一层一层的关联报错!
通过上述案例会发现这样的设计 代码可维护型很低,程序的耦合度⾮常⾼(修改⼀处代码,影响其他处的代码修改).
1.2.2 解决方案
尝试换⼀种思路,我们先设计汽⻋的⼤概样⼦,然后根据汽⻋的样⼦来设计⻋⾝,根据⻋⾝来设计 底盘,最后根据底盘来设计轮⼦.这时候,依赖关系就倒置过来了:轮⼦依赖底盘,底盘依赖⻋⾝, ⻋⾝依赖汽⻋。
我们可以尝试不在每个类中⾃⼰创建下级类,如果⾃⼰创建下级类就会出现当下级类发⽣改变操作, ⾃⼰也要跟着修改. 此时,我们只需要将原来由⾃⼰创建的下级类,改为传递的⽅式(也就是注⼊的⽅式),因为我们不 需要在当前类中创建下级类了,所以下级类即使发⽣变化(创建或减少参数),当前类本⾝也⽆需修 改任何代码,这样就完成了程序的解耦.
java">public class Main {public static void main(String[] args) {Tire tire = new Tire(17);Bottom bottom = new Bottom(tire);Framework framework = new Framework(bottom);Car car = new Car(framework);}
}//汽车类
class Car {private Framework tramework;public Car(Framework framework) {this.tramework = framework;System.out.println("Car init...");}
}//车身类
class Framework {private Bottom bottom;public Framework(Bottom bottom) {this.bottom = bottom;System.out.println("Tramework init...");}
}//地盘类
class Bottom {private Tire tire;public Bottom(Tire tire) {this.tire = tire;System.out.println("Bottom init...");}
}//轮胎类
class Tire {private int size;public Tire(int size) {this.size = size;System.out.println("Tire size : " + this.size);}
}
代码经过以上调整,⽆论底层类如何变化,整个调⽤链是不⽤做任何改变的,这样就完成了代码之间 的解耦,从⽽实现了更加灵活、通⽤的程序设计了。
1.2.3 IoC的优点
在传统的代码中对象创建顺序是:Car->Framework->Bottom->Tire
改进之后解耦的代码的对象创建顺序是:Tire->Bottom->Framework->Car
我们发现了⼀个规律,通⽤程序的实现代码,类的创建顺序是反的,传统代码是Car控制并创建了 Framework,Framework创建并创建了Bottom,依次往下,⽽改进之后的控制权发⽣的反转,不再是使⽤⽅对象创建并控制依赖对象了,⽽是把依赖对象注⼊将当前对象中,依赖对象的控制权不再由当前类控制了. 这样的话,即使依赖类发⽣任何改变,当前类都是不受影响的,这就是典型的控制反转,也就是IoC的 实现思想。
从上⾯也可以看出来,IoC容器具备以下优点:
1、资源不由使⽤资源的双⽅管理,⽽由不使⽤资源的第三⽅管理,这可以带来很多好处。第⼀,资源集 中管理,实现资源的可配置和易管理。
2、降低了使⽤资源双⽅的依赖程度,也就是我们说的耦合度。
3、资源集中管理: IoC容器会帮我们管理⼀些资源(对象等),我们需要使⽤时,只需要从IoC容器中 去取就可以了。
4、我们在创建实例的时候不需要了解其中的细节,降低了使⽤资源双⽅的依赖程度,也就是耦合 度。
1.3 DI 介绍
DI: DependencyInjection(依赖注⼊) 容器在运⾏期间,动态的为应⽤程序提供运⾏时所依赖的资源,称之为依赖注⼊。
依赖注⼊(DI)和控制反转(IoC)是从不同的⻆度的描述的同⼀件事情,依赖注⼊是从应⽤程序的⻆度来描述,就是指通过引⼊IoC容器,利⽤依赖关系注⼊的⽅式,实现对象之间的解耦.
上述代码中,是通过构造函数的⽅式,把依赖对象注⼊到需要使⽤的对象中的
IoC 是⼀种思想,也是"⽬标",⽽思想只是⼀种指导原则,最终还是要有可⾏的落地⽅案,⽽DI就属于 具体的实现。所以也可以说,DI是IoC的⼀种实现.
⽐如说我今天⼼情⽐较好,吃⼀顿好的犒劳犒劳⾃⼰,那么"吃⼀顿好的"是思想和⽬标(是 IoC),但最后我是吃海底捞还是杨国福?这就是具体的实现,就是DI。
二. IoC &DI使用
既然Spring是⼀个IoC(控制反转)容器,作为容器,那么它就具备两个最基础的功能:
• 存
• 取
Spring 容器管理的主要是对象,这些对象,我们称之为"Bean".我们把这些对象交由Spring管理,由 Spring来负责对象的创建和销毁.我们程序只需要告诉Spring,哪些需要存,以及如何从Spring中取出 对象
⽬标:把BookDao,BookService交给Spring管理,完成Controller层,Service层,Dao层的解耦
步骤:
1. Service层及Dao层的实现类,交给Spring管理: 使⽤注解: @Component
2. 在Controller层和Service层注⼊运⾏时依赖的对象:使⽤注解 @Autowired
实现:
1. 把BookDao交给Spring管理,由Spring来管理对象
java">package com.example.book.dao;import com.example.book.model.BookInfo;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;@Component
public class BookDao {public List<BookInfo> mockData() {//对于已知的数据量 建议初始化容量List<BookInfo> bookInfos = new ArrayList<>(15);for (int i = 1; i <= 15; i++) {BookInfo bookInfo = new BookInfo();bookInfo.setId(1);bookInfo.setBookName("图书" + i);bookInfo.setAuthor("作者" + i);bookInfo.setCount(new Random().nextInt(200));bookInfo.setPrice(new BigDecimal(new Random().nextInt(100)));bookInfo.setPublish("出版社" + i);bookInfo.setStatus(i % 5 == 0 ? 0 : 1);bookInfos.add(bookInfo);}return bookInfos;}
}
2.把BookService交给Spring管理,由Spring来管理对象
java">package com.example.book.service;import com.example.book.dao.BookDao;
import com.example.book.model.BookInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.List;@Component
public class BookService {@Autowiredprivate BookDao bookDao;public List<BookInfo> getBookList() {//1、获取图书数据//2、对图书的数据进行处理//3、返回数据//mock 表示虚拟假数据List<BookInfo> bookInfos =bookDao.mockData();for (BookInfo bookInfo : bookInfos) {if (bookInfo.getStatus() == 1) {bookInfo.setStatusCN("可借阅");} else {bookInfo.setStatusCN("不可借阅");}}return bookInfos;}
}
3.删除创建BookDao的代码,从Spring中获取对象
java">package com.example.book.service;import com.example.book.dao.BookDao;
import com.example.book.model.BookInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.util.List;@Component
public class BookService {@Autowiredprivate BookDao bookDao;public List<BookInfo> getBookList() {//1、获取图书数据//2、对图书的数据进行处理//3、返回数据//mock 表示虚拟假数据List<BookInfo> bookInfos =bookDao.mockData();for (BookInfo bookInfo : bookInfos) {if (bookInfo.getStatus() == 1) {bookInfo.setStatusCN("可借阅");} else {bookInfo.setStatusCN("不可借阅");}}return bookInfos;}
}
4. 删除创建BookService的代码,从Spring中获取对象
java">package com.example.book.controller;import com.example.book.model.BookInfo;
import com.example.book.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
@RequestMapping("/book")
public class BookController {@AutowiredBookService bookService ;@RequestMapping("/getBookList")public List<BookInfo> getBookList() {return bookService.getBookList();}
}