• Spring框架概述及核心设计思想


    一. Spring框架概述

    1. 什么是Spring框架

    我们通常所说的 Spring 指的是 Spring Framework(Spring 框架),它是⼀个开源框架,有着活跃而庞大的社区,这就是它之所以能长久不衰的原因;Spring 支持广泛的应用场景,它可以让 Java 企业级的应用程序开发起来更简单。

    用⼀句话概括 Spring:Spring 框架是包含了众多工具方法的 IoC 容器。

    2. 为什么要学习框架?

    因为学习框架相当于从“小作坊”到“工厂”的升级,小作坊什么都要自己做,工厂是组件式装配,特点就是高效。

    框架更加易⽤、简单且高效。

    Servlet有以下痛点:

    1. 添加外部 jar 不⽅便,容易出错,比如添加了⼀个不匹配的外部 jar 版本。
    2. 运行和调试的时候需要配置 Tomcat 不⽅便。
    3. 发布不方便,Servlet 项目必须依靠外置的 Tomcat(外置的 Web 容器)运行。
    4. 路由配置不方便,⼀个访问地址对应⼀个 Servlet 类。

    而 Spring Boot 相比于 Servlet 具备以下优点:

    1. 快速添加外部 jar 包。
    2. 调试项目更方便,无需配置 Tomcat,点击“运行”按钮就可以直接运行项目,因为 Spring Boot 内置了 Tomcat 容器可直接运行,但是 Servlet 需外挂 Tomcat。
    3. 发布项目更加方便,无需配置 Tomcat,使用 java -jar 方式就可以发布。
    4. 对象自动装配。
    5. 添加路由更加方便,无需每个访问地址都添加⼀个类。

    3. Spring框架学习的难点

    1. 配置比较多。
    2. 需要⼤量的外部 jar 包,在下载时容易出错。
    3. 会涉及简单的软件⼯程的设计思想(分层思想:前后端的分层思想;后端工程的分层思想)。
    4. 知识点相对来说比之前的知识更加的分散,要仔细听才能搞懂各个知识点的逻辑关系。
    5. 要记的东西很多,所以要大量地重复练习才能记住,比如各种注解。

    Spring框架基本学习应用路线:Spring全家桶(Spring/Spring Boot/Spring MVC) -> MyBatis -> Redis 等。

    二. Spring核心设计思想

    Spring 核心就是这么一句话:Spring 框架是包含了众多工具方法的 IoC 容器。

    那么这句话怎么理解呢?什么是容器?什么又是 IoC?

    1. 容器是什么?

    容器是用来容纳某种物品的(基本)装置。 ——来⾃:百度百科

    Java 中也有一些容器,比如 List,Map,Set 等这些集合,是属于数据储存的容器,它把我们常用的操作都封装到集合中了,我们只需要知道集合为我们提供的方法就可以使用各种集合了;还有 Tomcat 是属于 Web 容器,同理 Spring 是就一个 IoC 容器,它包含了许多的工具和方法。

    2. IoC是什么?

    IoC 即 Inversion of Control,直译过来就是控制反转的意思,这是一种思想,控制权反转,在 Java 的常规代码中,对象的生命周期,是由当前代码(程序员自己)控制的,而控制权反转就是对象的生命周期,不再由当前代码片段来控制,而是由 Spring(IoC 容器)来控制 。

    这种思想还比较抽象,我们来通过一个例子来了解它。

    我们都知道汽车,它包含轮胎,底座,车身等,现在我们要造一辆汽车时,需要有车身,而车身需要有底座和轮胎,最传统的思想就是造车时,需要车身,于是就new一个车身,而车身需要底座,于是就再new一个底座,同理底座需要轮胎,那就造底座前new一个轮胎。
    img

    我们可以得到以下代码:

    package old;
    
    /**
     * 传统开发方式, 耦合性问题
     */
    // 汽车类
    public class Car {
        // 车身
        private Framework framework;
    
        public Car() {
            framework = new Framework();
        }
    
        public void init() {
            System.out.println("do car");
            // 汽车的组建依赖于车身
            framework.init();
        }
    }
    
    package old;
    
    // 车身类
    public class Framework {
        private Bottom bottom;
    
        public Framework() {
            bottom = new Bottom();
        }
    
        public void init() {
            System.out.println("do framework");
            // 车身的组建依赖于底盘
            bottom.init();
        }
    }
    
    package old;
    
    // 底盘类
    public class Bottom {
        private Tire tire;
    
        public Bottom() {
            tire = new Tire();
        }
    
        public void init() {
            System.out.println("do bottom");
            // 底盘的组建依赖于轮胎
            tire.init();
        }
    }
    
    package old;
    
    // 轮胎类
    public class Tire {
        private int size = 17; // 轮胎的尺寸
        public void init() {
            System.out.println("size -> " + size);
        }
    }
    
    package old;
    
    public class Test {
        public static void main(String[] args) {
            Car car = new Car();
            car.init();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    但是,但上面的代码中,轮胎的大小是固定写死的, 然而随着对的车的需求量越来越大,个性化需求也会越来越多,这 时候我们就需要加⼯多种尺寸的轮胎,那这个时候就要对上面的程序进行修改了,根据我们上面写的代码,我们需要往轮胎Tire类的构造方法加上一个参数,由于底盘Bottom类控制着Tire类,那么底盘类的构造方法也需要加上一个参数,以此类推,我们的车身Framework类与汽车Car类都需要为构造方法加上参数,于是我们得到了如下的代码:

    package old;
    
    /**
     * 传统开发方式, 耦合性问题
     */
    // 汽车类
    public class Car {
        // 车身
        private Framework framework;
    
        public Car(int size) {
            framework = new Framework(size);
        }
    
        public void init() {
            System.out.println("do car");
            // 汽车的组建依赖于车身
            framework.init();
        }
    }
    
    package old;
    
    // 车身类
    public class Framework {
        private Bottom bottom;
    
        public Framework(int size) {
            bottom = new Bottom(size);
        }
    
        public void init() {
            System.out.println("do framework");
            // 车身的组建依赖于底盘
            bottom.init();
        }
    }
    
    package old;
    
    // 底盘类
    public class Bottom {
        private Tire tire;
    
        public Bottom(int size) {
            tire = new Tire(size);
        }
    
        public void init() {
            System.out.println("do bottom");
            // 底盘的组建依赖于轮胎
            tire.init();
        }
    }
    
    package old;
    
    // 轮胎类
    public class Tire {
        private int size = 17; // 轮胎的尺寸
    
        public Tire(int size) {
            this.size = size;
        }
    
        public void init() {
            System.out.println("size -> " + size);
        }
    }
    
    package old;
    
    public class Test {
        public static void main(String[] args) {
            Car car = new Car(20);
            car.init();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    此时,如果需要个性化定制轮胎的大小,就可以只改动构造Car对象传入的参数就可以了;但是,如果再加上加上一个需求,还要定制轮胎的颜色,那我们又要加参数,此时就意味着像上面一样修改最底层的代码, 整个调⽤链上的所有代码都需要修改。

    这样的代码耦合性就太高了,为了解决这个问题,我们可以使用loC的思想来实现代码,将控制权交出去,也就是说,IoC模式下,我们不再自己构造创建对象,当我们需要轮胎Tire类时,你就给我传一个Tire对象,我们不去new一个Tire对象了,这样的话,就算在Tire类加参数也只需要改动Tire类的构造方法与相关执行方法与属性,顶多再改一下Tire对象的创建,同理其他类也一样,将对象作为参数传入到上级类的构造方法中去就行了,但此时其他类是不需要修改的,这个过程也叫做传入或注入
    img
    由于我们创建Car时需要Framework,所以先要实例一个Framework对象,同理实例一个Framework对象需要Bottom对象,那么需先实例一个Bottom对象,一样,在实例Bottom对象之前需要实例一个Tire对象,于是需要先后创建Tire对象,Bottom对象,Framework对象后才能创建一个Car对象,我们可以得到如下的代码:

    package ioc;
    
    public class Car {
        // 汽车的组建依赖于车身的组建
        private Framework franmework;
    
        public Car(Framework franmework) {
            this.franmework = franmework;
        }
        public void init() {
            System.out.println("do car...");
            franmework.init();
        }
    }
    
    package ioc;
    
    public class Framework {
        // 车身的组建依赖于底盘
        private Bottom bottom;
    
        public Framework(Bottom bottom) {
            this.bottom = bottom;
        }
        public void init() {
            System.out.println("do franmework");
            bottom.init();
        }
    }
    
    package ioc;
    
    public class Bottom {
        // 底盘的组建依赖于轮胎
        private Tire tire;
    
        public Bottom(Tire tire) {
            this.tire = tire;
        }
    
        public void init() {
            System.out.println("do bottom...");
            tire.init();
        }
    }
    
    package ioc;
    
    public class Tire {
        private int size = 17;
        private String color = "黑色";
    
        public Tire(int size, String color) {
            this.size = size;
            this.color = color;
        }
    
        public void init() {
            System.out.println("size -> " + size);
            System.out.println("color->" + color);
        }
    }
    
    package ioc;
    
    public class IOCDemo {
        // 这里的内容包含就相当于是 IoC 容器做的事情
        // 对象的生命周期控制权就翻转给 IoC 容器了, 不再由程序员控制
        private Tire tire;
        private Bottom bottom;
        private Framework framework;
        public Car car;
        public IOCDemo() {
            tire = new Tire(17, "红色");
            bottom = new Bottom(tire);
            framework = new Framework(bottom);
            car = new Car(framework);
        }
    }
    
    package ioc;
    
    /**
     * 模拟 IoC 容器
     */
    public class Test {
        public static void main(String[] args) {
            // 直接使用, 创建就交给IoC了
            IOCDemo ioc = new IOCDemo();
            Car car = ioc.car;
            car.init();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93

    此时如果要变需求,需要加参数或减少参数,IoC 的代码只需改动图中的两处代码即可, 整个调用链是不用做任何改变的, 达到了解耦的目的。

    img
    img

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

    到这里我们就可以发现,传统的代码类创建顺序是反着的,Car 控制 FrameWork,FrameWork 控制着 Bottom,Bottom 控制着 Tire;而改进之后的控制权发生了反转,是下层将对象注入到当前对象当中,下级的控制权不再由上级控制了,下级在发生改变时会将改变完成后的对象注入上级,这样上级就是不受影响的,这就是 IoC 的实现思想。

    所以 IoC 有以下的优点:对象(Bean)的生命周期交给 IoC 框架维护,作为程序员无需关注,说白了就是程序员不需要关注对象创建、销毁时机以及对象的依赖关系,这些工作加个 IoC 框架(也就是 Spring)做就行,实现了代码的解耦,对象的使用更加方便高效。

    3. Spring是IoC容器

    Spring 框架就是包含了多个工具方法的 IoC 容器,既然是容器,那它就有存和取的功能,这也是 Spring 最核心的两个功能:

    1. 将 Bean(对象)存储到 Spring 容器中。
    2. 将 Bean(对象)从 Spring 容器中取出来。

    将对象存放到容器有什么好处呢?
    将对象存储到 IoC 容器相当于我们将所有可能用到的工具制作好都放到仓库,当我们需要使用时直接取即可,用完归还仓库;而 new 对象的方式相当于我们每次需要用工具的时候现场制作,制作完了扔掉,下次需要的时候重新做。

    Spring 是⼀个 IoC 容器,说的是对象的创建和销毁的权利都交给 Spring 来管理了,它本身⼜具备了存储对象和获取对象的能力。

    4. DI(依赖注入)

    DI,即Dependency Injection,依赖注入。

    所谓依赖注⼊,就是由 IoC 容器在运行期间,动态地将某种依赖关系注入到对象之中,在pom.xml有一个依赖项,就是用来导入外部的资源,而这里的依赖注入,导入的不是外部的资源,而是对象;当某个 Java 实例需要另一个 Java 实例时,创建被调用者的工作不是由调用者实现,而是由 Spring 容器来完成,然后注入调用者,因此称为依赖注入。

    IoC 与 DI 的区别是什么?

    依赖注入(DI)和控制反转(IoC)是从不同的角度的描述的同⼀件事情,就是指通过引入 IoC 容器,利用依赖关系注入的方式,实现对象之间的解耦。

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

    5. DL(依赖查找)

    DL,即Dependency Lookup,依赖查找,也是 IoC的一种实现。

    依赖查找和依赖注入的区别在于,依赖注入是将依赖关系委托给容器,由容器来管理对象之间的依赖关系,容器外部是不关心这种依赖关系的,需要时由容器判断提供什么;而依赖查找是由对象自己来查找它所依赖的对象,容器只负责管理对象的生命周期,也就是说此时需要容器外部自己确定要容器提供哪种依赖关系;两者之间是主动和被动的区别。

  • 相关阅读:
    自主移动机器人模型制作
    Make 详解
    springMVC视图、视图解析器、国际化笔记
    go Jenkins流水线极速打包镜像上传
    关于java8新特性 Stream流的常规用法总结
    【微服务】服务发现和管理技术框架选型调研
    【深基16.例1】淘汰赛(上)
    k8s 读书笔记 - kubernetes 基本概念和术语(上)
    HiEV独家 | 接棒余承东,华为光产品线总裁靳玉志出任车BU CEO
    NPM 常用命令(二)
  • 原文地址:https://blog.csdn.net/Trong_/article/details/131750723