目录
Spring 的全称是 Spring Framework, 它是一种开源框架, 2002 年, Rod Jahnson 首次推出了 Spring 框雏形 interface21 框架. 两年后, Spring 框架以 interface21 框架为基础, 经过重新设计, 发布了 1.0 正式版....给软件行业带来了春天!!
用一句话概括 Spring: Spring 是一个包含了众多工具方法的 IoC 容器.
所谓容器, 通俗来说就是容纳某种物品的装置, 在学数据结构的时候, 我们的各种集合就叫做容器, 例如 List, Map 等等用于存储数据的这些集合不就是一种容器吗, 还有我们在学 Sevlet 的时候, 用到的 Tomcat 也是一个容器, 它是用来存放每一个 Web 项目的.
说 Spring 是一个 IoC 容器, 那什么又是 IoC 呢 ?
IoC 全称 -> Inversion of Control , 翻译成中文就是 "控制(权)反转的意思" !
>> 如何理解控制(权)反转?
简言之就是把控制权交给别人了, 自己不需要管了.
>> 控制(权)反转了, 是否就意味着自己不能控制了?
控制(权)反转后, 自己还是可以通过一些手段来进行控制, 但是没有必要. 这就好比我们的手动挡的车和自动挡的车, 在没有自动挡的时代, 我们需要踩离合, 手动挂挡, 拉手刹等等操作, 这些操作都由我们自己控制; 当有了自动挡之后, 我们就可以把控制权交给发动机, 交给引擎来控制, 此时你还会想要自己去控制吗, 完全没有必要...
说一千道一万, 什么是 "控制反转" , 好像还是没有说太明白, 接下来我们通过 "传统式开发" 和 "控制(权)反转式开发" 二者的代码对比来进行理解!!
假如现在我们想要造一辆汽车, 造汽车依赖于车身, 造车身又依赖于底盘, 造底盘又依赖于轮胎.....
【代码示例】
2.1 Class 汽车
- public class Car {
- private Framework framework;
-
- public Car() {
- framework = new Framework();
- }
- public static void main(String[] args) {
- // 构建并启动一辆车
- Car car = new Car();
- car.init();
- }
- // 运行
- public void init() {
- System.out.println("Car init.");
- // 依赖于车身 -> framework -> init()
- framework.init();
- }
- }
2.2 Class 车身
- public class Framework {
- private Bottom bottom;
-
- public Framework() {
- bottom = new Bottom();
- }
- public void init() {
- System.out.println("Framework init.");
- // 依赖于底盘 -> bottom -> init()
- bottom.init();
- }
- }
2.3 Class 底盘
- public class Bottom {
- private Tire tire;
-
- public Bottom() {
- tire = new Tire();
- }
- public void init() {
- System.out.println("Bottom init.");
- // 依赖于轮胎 -> tire -> init()
- tire.init();
- }
- }
2.4 Class 轮胎
- public class Tire {
- // 尺寸
- private int size = 16;
-
- public void init() {
- System.out.println("Tire size:" + size);
- }
- }
在传统时代, 假设人们都只需要车轮大小为 16 尺寸的汽车, 此时产品经理就告诉生产线上的负责人说造 16 尺寸的轮胎就行了, 随着物质水平的提高, 人们个性化追求是必然的趋势, 有些人就可能不满足于只有 16 尺寸轮胎的汽车, 这时候就需要修改代码, 给轮胎类中的构造方法加一个 size 参数, 你想要多大尺寸的轮胎, 你传给我就好了, 接下来看代码:
- public class Tire {
- // 尺寸
- private int size = 16;
-
- public Tire(int size) {
- this.size = size;
- }
-
- public void init() {
- System.out.println("Tire size:" + size);
- }
- }
>>当我给轮胎的构造方法中加上一个参数之后, 前面有着依赖关系的底盘代码就报错了, 并且整个依赖链上的所有代码都需要做出改变, 这就是传统开发的问题所在了. 代码的耦合性太强.这还只是四层调用关系的代码, 并且只修改了一个参数, 如果是十几层调用关系的代码, 并且修改多个参数, 可想而知通过传统的方式传参的问题有多大了.
>>当客户的需求越来越多时, 我该如何解决:
我们可以将控制权交给别人, 也就是相当于把轮胎, 车身, 以及其他部分外包给别人来做, 我们就不用再去关心客户的这些多种多样的需求了. 只需要向代理厂商下订单就好了. 所以就需要用到控制(权)反转式开发.
这种方式的开发, 传递的参数就不再是轮胎的尺寸, 材质以及车身的颜色, 而是直接将整个轮胎对象当做参数进行传递, 轮胎, 车身怎么变化我不再关心了, 控制权我交给厂商.
【代码示例】
App(测试代码)
- public class App {
- public static void main(String[] args) {
- // 程序调用
- int size = 17;
- String color = "黑色";
- String wood = "铝合金";
- TireV2 tireV2 = new TireV2(size, color, wood);
- BottomV2 bottomV2 = new BottomV2(tireV2);
- FrameworkV2 frameworkV2 = new FrameworkV2(bottomV2);
- CarV2 carV2 = new CarV2(frameworkV2);
- carV2.init();
- }
- }
3.1 Class 汽车
- public class CarV2 {
- private FrameworkV2 frameworkV2;
-
- public CarV2(FrameworkV2 frameworkV2) {
- // frameworkV2 = new FrameworkV2(); // 舍弃自己创建车身这种写法
- this.frameworkV2 = frameworkV2; // 控制权交给别了
- }
- public void init() {
- System.out.println("Car v2 init.");
- // 依赖于车身 -> frameworkV2 -> init()
- frameworkV2.init();
- }
- }
3.2 Class 车身
- public class FrameworkV2 {
-
- private BottomV2 bottomV2;
-
- public FrameworkV2(BottomV2 bottomV2) {
- this.bottomV2 = bottomV2;
- }
- public void init() {
- System.out.println("Framework v2 init.");
- // 依赖于底盘 -> bottomV2 -> init()
- bottomV2.init();
- }
- }
3.3 Class 底盘
- public class BottomV2 {
- private TireV2 tireV2;
- public BottomV2(TireV2 tireV2) {
- this.tireV2 = tireV2;
- }
- public void init() {
- System.out.println("Bottom V2 init.");
- // 依赖于轮胎 -> tireV2 -> init()
- tireV2.init();
- }
- }
3.4 Class 轮胎
- public class TireV2 {
- private int size;
- private String color;
- private String wood;
-
- public TireV2(int size, String color, String wood) {
- this.size = size;
- this.color = color;
- this.wood = wood;
- }
- public void init() {
- System.out.println("Tire v2 size:" + size);
- }
- }
这种方式的代码, 就算客户需求再怎么多变, 我的代码也只是需要更改整个依赖链上最底层依赖的代码和测试代码即可, 中间的代码都不需要改变.
对比两种开发模式:
>> 传统开发: 开发一辆车的时候, 我们先是创建 Car 对象, 然后再创建 Framework , Bottom ....对象, 是从上往下创建的,并且控制关系是上级对象直接控制着下级对象。
>> 控制反转开发: 先创建下级对象, 然后才会创建上级对象, 并且是把下级对象作为参数注入到上级对象去使用的, 此时下级对象的控制权就不在上级对象手里了, 这就是典型的控制反转思想 (IoC 思想)
经过前面的讲解, 我们已经知道 Spring 是一个包含了众多工具方法的控制反转的 IoC 容器 , 那么既然是一个容器的话, 它最基本的功能又是什么呢? 我们学的数据结构中的集合就是用于存取数据的, 此处的 Spring 最核心的两个功能也就是存储对象到 Spring 中 和 从Spring 中获取对象.
【对比】为什么要使用 Spring 存储对象和获取对象,这相比于普通的 new 的方式有什么优势呢 ?
通过的 new 的方式创建和使用对象时, 当每次需要时, 就去 new , 用完了就不要了也不会保存, 下次还要使用的时候还得 new , 同样的事情要做很多遍。
而 Spring 是一个 IoC 容器, 把对象存储进去之后, 我想用的时候, 就从里面拿, 不想用的时候又放回去, 并且对象的创建和销毁的都交给 Spring 来管理了, 这样就很方便.
【谈谈 DI】
谈到 IoC, 就免不了谈到 "DI", 什么是 DI, DI 全称 Dependency Injection. 中文意思就是 "依赖注入".
什么是依赖注入?
依赖注入就是当程序在 IoC 容器中运行期间, 动态的将所需要的对象或者是某种依赖关系动态的注入到对象中. 简单来说和从 Spring 中获取出来对象差不多, 本身 "依赖注入" 和 "控制反转" 就是在描述同一件事情(引入 IoC 容器, 利用依赖关系, 实现对象之间解耦), 只不过角度不同而已.
IoC 是一种 "思想"(/目标), 而 DI 更是一种具体的实现!!
下篇博客就开始进入 Spring 正题咯~