一. spring框架概述
1. 什么是spring框架
我们通常所说的 spring 指的是 spring framework(spring 框架),它是⼀个开源框架,有着活跃而庞大的社区,这就是它之所以能长久不衰的原因;spring 支持广泛的应用场景,它可以让 java 企业级的应用程序开发起来更简单。
用⼀句话概括 spring:spring 框架是包含了众多工具方法的 ioc 容器。
2. 为什么要学习框架?
因为学习框架相当于从“小作坊”到“工厂”的升级,小作坊什么都要自己做,工厂是组件式装配,特点就是高效。
框架更加易⽤、简单且高效。
servlet有以下痛点:
- 添加外部 jar 不⽅便,容易出错,比如添加了⼀个不匹配的外部 jar 版本。
- 运行和调试的时候需要配置 tomcat 不⽅便。
- 发布不方便,servlet 项目必须依靠外置的 tomcat(外置的 web 容器)运行。
- 路由配置不方便,⼀个访问地址对应⼀个 servlet 类。
- …
而 spring boot 相比于 servlet 具备以下优点:
- 快速添加外部 jar 包。
- 调试项目更方便,无需配置 tomcat,点击“运行”按钮就可以直接运行项目,因为 spring boot 内置了 tomcat 容器可直接运行,但是 servlet 需外挂 tomcat。
- 发布项目更加方便,无需配置 tomcat,使用 java -jar 方式就可以发布。
- 对象自动装配。
- 添加路由更加方便,无需每个访问地址都添加⼀个类。
- …
3. spring框架学习的难点
- 配置比较多。
- 需要⼤量的外部 jar 包,在下载时容易出错。
- 会涉及简单的软件⼯程的设计思想(分层思想:前后端的分层思想;后端工程的分层思想)。
- 知识点相对来说比之前的知识更加的分散,要仔细听才能搞懂各个知识点的逻辑关系。
- 要记的东西很多,所以要大量地重复练习才能记住,比如各种注解。
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
一个轮胎。
我们可以得到以下代码:
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(); } }
但是,但上面的代码中,轮胎的大小是固定写死的, 然而随着对的车的需求量越来越大,个性化需求也会越来越多,这 时候我们就需要加⼯多种尺寸的轮胎,那这个时候就要对上面的程序进行修改了,根据我们上面写的代码,我们需要往轮胎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(); } }
此时,如果需要个性化定制轮胎的大小,就可以只改动构造car
对象传入的参数就可以了;但是,如果再加上加上一个需求,还要定制轮胎的颜色,那我们又要加参数,此时就意味着像上面一样修改最底层的代码, 整个调⽤链上的所有代码都需要修改。
这样的代码耦合性就太高了,为了解决这个问题,我们可以使用loc
的思想来实现代码,将控制权交出去,也就是说,ioc
模式下,我们不再自己构造创建对象,当我们需要轮胎tire
类时,你就给我传一个tire
对象,我们不去new
一个tire
对象了,这样的话,就算在tire
类加参数也只需要改动tire
类的构造方法与相关执行方法与属性,顶多再改一下tire
对象的创建,同理其他类也一样,将对象作为参数传入到上级类的构造方法中去就行了,但此时其他类是不需要修改的,这个过程也叫做传入或注入。
由于我们创建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(); } }
此时如果要变需求,需要加参数或减少参数,ioc 的代码只需改动图中的两处代码即可, 整个调用链是不用做任何改变的, 达到了解耦的目的。
- 在传统的代码中对象创建顺序是: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 最核心的两个功能:
- 将 bean(对象)存储到 spring 容器中。
- 将 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的一种实现。
依赖查找和依赖注入的区别在于,依赖注入是将依赖关系委托给容器,由容器来管理对象之间的依赖关系,容器外部是不关心这种依赖关系的,需要时由容器判断提供什么;而依赖查找是由对象自己来查找它所依赖的对象,容器只负责管理对象的生命周期,也就是说此时需要容器外部自己确定要容器提供哪种依赖关系;两者之间是主动和被动的区别。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论