一、快速上手 SpringBoot
学习任意一项技术,首先要知道这个技术的作用是什么,不然学完以后,你都不知道什么时候使用这个技术,也就是技术对应的应用场景。SpringBoot 技术由 Pivotal 团队研发制作,功能的话简单概括就是加速 Spring 程序的开发,这个加速要从如下两个方面来说
- Spring 程序初始搭建过程
- Spring 程序的开发过程
通过上面两个方面的定位,我们可以产生两个模糊的概念:
- SpringBoot 开发团队认为原始的 Spring 程序初始搭建的时候可能有些繁琐,这个过程是可以简化的,那原始的 Spring 程序初始搭建过程都包含哪些东西了呢?为什么觉得繁琐呢?最基本的 Spring 程序至少有一个配置文件或配置类,用来描述 Spring 的配置信息,莫非这个文件都可以不写?此外现在企业级开发使用 Spring 大部分情况下是做 Web 开发,如果做 Web 开发的话,还要在加载 Web 环境时加载指定的 Spring 配置,这都是最基本的需求了,不写的话怎么知道加载哪个配置文件 / 配置类呢?那换了 SpringBoot 技术以后呢,这些还要写吗?谜底稍后揭晓,先卖个关子
- SpringBoot 开发团队认为原始的 Spring 程序开发的过程也有些繁琐,这个过程仍然可以简化。开发过程无外乎使用什么技术,导入对应的 jar 包(或坐标)然后将这个技术的核心对象交给 Spring 容器管理,也就是配置成 Spring 容器管控的 bean 就可以了。这都是基本操作啊,难道这些东西 SpringBoot 也能帮我们简化?
带着上面这些疑问我们就着手第一个 SpringBoot 程序的开发了,看看到底使用 SpringBoot 技术能简化开发到什么程度。
1、SpringBoot 入门程序制作(一)
下面让我们开始做第一个 SpringBoot 程序吧,本课程基于 IDEA 2020.3 版本制作,使用的 Maven 版本为 3.6.1,JDK版本为 1.8。如果你的环境和上述环境不同,可能在操作界面和操作过程中略有不同,只要软件匹配兼容即可(说到这个 IDEA 和 Maven,它们两个还真不是什么版本都能搭到一起的,说多了都是泪啊)。
下面使用 SpringBoot 技术快速构建一个 SpringMVC 的程序,通过这个过程体会简化二字的含义
步骤 ①:创建新模块,选择 Spring Initializr,并配置模块相关基础信息

特别关注:第 3 步点击 Next 时,IDEA 需要联网状态才可以进入到后面那一页,如果不能正常联网,就无法正确到达右面那个设置页了,会一直联网转转转
特别关注:第 5 步选择 Java 版本和你计算机上安装的 JDK 版本匹配即可,但是最低要求为 JDK8 或以上版本,推荐使用 8 或 11
步骤 ②:选择当前模块需要使用的技术集

按照要求,左侧选择 Web,然后在中间选择 Spring Web 即可,选完右侧就出现了新的内容项,表示勾选成功了
关注:此处选择的 SpringBoot 的版本使用默认的就可以了,需要说一点,SpringBoot 的版本升级速度很快,可能昨天创建工程的时候默认版本是 2.5.4,今天再创建工程默认版本就变成 2.5.5 了,差别不大,无需过于纠结,回头可以到配置文件中修改对应的版本
步骤 ③:开发控制器类
// Rest模式
@RestController
@RequestMapping("/books")
public class BookController {
@GetMapping
public String getById() {
System.out.println("springboot is running...");
return "springboot is running...";
}
}
入门案例制作的 SpringMVC 的控制器基于 Rest 风格开发,当然此处使用原始格式制作 SpringMVC 的程序也是没有问题的,上例中的 @RestController 与 @GetMapping 注解是基于 Restful 开发的典型注解
关注:做到这里 SpringBoot 程序的最基础的开发已经做完了,现在就可以正常的运行 Spring 程序了。可能有些小伙伴会有疑惑,Tomcat 服务器没有配置,Spring 也没有配置,什么都没有配置这就能用吗?这就是 SpringBoot 技术的强大之处。关于内部工作流程后面再说,先专心学习开发过程
步骤 ④:运行自动生成的 Application 类

使用带 main 方法的 Java 程序的运行形式来运行程序,运行完毕后,控制台输出上述信息。
不难看出,运行的信息中包含了 8080 的端口、Tomcat 这种熟悉的字样,难道这里启动了 Tomcat 服务器?是的,这里已经启动了。那服务器没有配置,哪里来的呢?后面再说。现在你就可以通过浏览器访问请求的路径,测试功能是否工作正常了
访问路径:http://localhost:8080/books
是不是感觉很神奇?目前的效果其实依赖的底层逻辑还是很复杂的,但是从开发者角度来看,目前只有两个文件展现到了开发者面前,即最简 SpringBoot 程序所包含的基础文件:
- pom.xml:这是 Maven 的配置文件,描述了当前工程构建时相应的配置信息
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>springboot_01_01_quickstart</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
配置中有两个信息需要关注,一个是 parent,也就是当前工程继承了另外一个工程,干什么用的后面再说,还有依赖坐标,干什么用的后面再说
- Application 类
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
这个类功能很简单,就一句代码,前面运行程序就是运行的这个类
到这里我们可以大胆推测一下,如果上面这两个文件没有的话,SpringBoot 肯定没法玩,看来核心就是这两个文件了。由于是制作第一个 SpringBoot 程序,先不要关注这两个文件的功能,后面详细讲解内部工作流程。
通过上面的制作,我们不难发现,SpringBoot 程序简直太好写了,几乎什么都没写,功能就有了,这也是SpringBoot 技术为什么现在这么火的原因,和 Spirng 程序相比,SpringBoot 程序在开发的过程中各个层面均具有优势。
| 类配置文件 | Spring | SpringBoot |
|---|---|---|
| pom 文件中的坐标 | 手工添加 | 勾选添加 |
| Web 3.0 配置类 | 手工制作 | 无 |
| Spring / SpringMVC 配置类 | 手工制作 | 无 |
| 控制器 | 手工制作 | 手工制作 |
一句话总结一下就是能少写就少写,能不写就不写,这就是 SpringBoot 技术给我们带来的好处,行了,现在你就可以动手做一做 SpringBoot 程序了,看看效果如何,是否真的帮助你简化开发了
注意:基于 IDEA 开发 SpringBoot 程序需要确保联网且能够加载到程序框架结构
总结
- 开发 SpringBoot 程序可以根据向导进行联网快速制作
- SpringBoot 程序需要基于 JDK8 以上版本进行制作
- SpringBoot 程序中需要使用何种功能通过勾选选择技术,也可以手工添加对应的要使用的技术(后期讲解)
- 运行 SpringBoot 程序通过运行 Application 程序入口进行
思考
前面制作的时候说过,这个过程必须联网才可以进行,但是有些时候你会遇到一些莫名其妙的问题,比如基于 IDEA 开发时,你会发现你配置了一些坐标,然后 Maven 下载对应东西的时候死慢死慢的,甚至还会失败。其实这和 IDEA 这款 IDE 工具有关,万一 IDEA 不能正常访问网络的话,我们是不是就无法制作 SpringBoot 程序了呢?咱们下一节再说
2、SpringBoot 入门程序制作(二)
如果 IDEA 不能正常联网,这个 SpringBoot 程序就无法制作了吗?开什么玩笑,世上 IDE 工具千千万,难道SpringBoot 技术还必须基于 IDEA 来做了?这是不可能的。开发 SpringBoot 程序,可以不基于任意的 IDE 工具进行,其实在 SpringBoot 的官网里面就可以直接创建 SpringBoot 程序
SpringBoot 官网和 Spring 的官网是在一起的,都是 https://spring.io。你可以通过项目一级一级的找到 SpringBoot 技术的介绍页,然后在页面中间部位找到如下内容

步骤 ①:点击 Spring Initializr 后进入到创建 SpringBoot 程序的界面上,下面是输入信息的过程,和前面的一样,只是界面变了而已,根据自己的要求,在左侧选择对应信息和输入对应的信息即可

步骤 ②:右侧的 ADD DEPENDENCIES 用于选择使用何种技术,和之前勾选的 Spring Web 是在做同一件事,仅仅是界面不同而已,点击后打开网页版的技术选择界面

步骤 ③:所有信息设置完毕后,点击下面左侧按钮,生成一个文件包

步骤 ④:保存后得到一个压缩文件,这个文件打开后就是创建的 SpringBoot 工程文件夹了

步骤 ⑤:解压缩此文件后,得到工程目录,在 IDEA 中导入即可使用,和之前创建的东西完全一样。下面就可以自己创建一个 Controller 测试一下是否能用了。
温馨提示
做到这里其实可以透漏一个小秘密,IDEA 工具中创建 SpringBoot 工程其实连接的就是 SpringBoot 的官网,走的就是这个过程,只不过 IDEA 把界面给整合了一下,读取到了 Spring 官网给的信息,然后展示到了 IDEA 的界面中而已,不信你可以看看下面这个步骤

上面描述了连接的网址,再看看 SpringBoot 官网创建工程的 URL 地址,是不是一样?

总结
- 打开 SpringBoot 官网,选择 Quickstart Your Project

- 创建工程,并保存项目

- 解压项目,通过 IDE 导入项目
思考
现在创建工程靠的是访问国外的 Spring 主站,但是互联网访问是可以控制的,如果一天这个网站你在国内都无法访问了,那前面这两种方式都无法创建 SpringBoot 工程了,这时候又该怎么解决这个问题呢?咱们下一节再说
3、SpringBoot 入门程序制作(三)
前面提到网站如果被限制访问了,该怎么办?开动脑筋想一想,不管是方式一还是方式二其实都是走的同一个地方,也就是 SpringBoot 的官网创建的 SpringBoot 工程,那如果我们国内有这么一个网站能提供这样的功能,是不是就解决了呢?必然的嘛,新的问题又来了,这个国内的网站有吗?还真有,阿里提供了一个,下面问题就简单了,网址告诉我们就 OK 了,没错就是这样
创建工程时,切换选择 starter 服务路径,然后手工输入阿里云提供给我们的使用地址即可。地址:http://start.aliyun.com 或 https://start.aliyun.com

阿里为了便于自己开发使用,因此在依赖坐标中添加了一些阿里相关的技术,也是为了推广自己的技术吧,所以在依赖选择列表中,你有了更多的选择。不过有一点需要说清楚,阿里云地址默认创建的 SpringBoot 工程版本是 2.4.1,所以如果你想更换其他的版本,创建项目后手工修改即可,别忘了刷新一下,加载新版本信息

阿里云提供的地址更符合国内开发者的使用习惯,里面有一些 SpringBoot 官网上没有给出的坐标,大家可以好好看一看。
注意:阿里云提供的工程创建地址初始化完毕后和使用 SpringBoot 官网创建出来的工程略有区别。主要是在配置文件的形式上有区别。这个信息在后面讲解 Boot 程序的执行流程时给大家揭晓
总结
- 选择 start 来源为自定义 URL
- 输入阿里云 start 地址
- 创建项目
思考
做到这里我们已经有了三种方式创建 SpringBoot 工程,但是每种方式都要求你必须能上网才能创建工程。假如有一天,你加入了一个保密级别比较高的项目组,整个项目组没有外网,整个事情是不是就不能做了呢?咱们下一节再说
4、SpringBoot 入门程序制作(四)
不能上网,还想创建 SpringBoot 工程,能不能做呢?能做,但是你要先问问自己联网和不联网到底差别是什么?这个信息找到以后,你就发现,你把联网要干的事情都提前准备好,就无需联网了。
联网做什么呢?首先 SpringBoot 工程也是基于 Maven 构建的,而 Maven 工程当使用了一些自己需要使用又不存在的东西时,就要去下载。其实 SpringBoot 工程创建的时候就是去下载一些必要的组件的。你把这些东西给提前准备好就可以了吗?是的,就是这样。
下面咱们就一起手工创建一个 SpringBoot 工程
步骤 ①:创建工程时,选择手工创建 Maven 工程

步骤 ②:参照标准 SpringBoot 工程的 pom.xml 文件,书写自己的 pom.xml 文件即可
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
</parent>
<groupId>com.itheima</groupId>
<artifactId>springboot_01_04_quickstart</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
用什么写什么,不用的都可以不写。当然,现在小伙伴们可能还不知道用什么和不用什么,最简单的就是复制粘贴了,随着后面的学习,你就知道哪些可以省略了。此处我删减了一些目前不是必须的东西,一样能用
步骤 ③:之前运行 SpringBoot 工程需要一个引导类,这个缺不了,自己手写一个就行了,建议按照之前的目录结构来创建,先别玩花样,先学走后学跑。类名可以自定义,关联的名称一切修改即可
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
关注:类上面的注解 @SpringBootApplication 千万别丢了,这个是核心,后面再介绍
关注:类名可以自定义,只要保障下面代码中使用的类名和你自己定义的名称一样即可,也就是 run 方法中的那个class 对应的名称
步骤 ④:下面就可以自己创建一个 Controller 测试一下是否能用了,和之前没有差别了
看到这里其实应该能够想明白了,通过向导或者网站创建的 SpringBoot 工程其实就是帮你写了一些代码,而现在是自己手写,写的内容都一样,仅此而已。
温馨提示
如果你的计算机上从来没有创建成功过 SpringBoot 工程,自然也就没有下载过 SpringBoot 对应的坐标,那用手写创建的方式在不联网的情况下肯定该是不能用的。所谓手写,其实就是自己写别人帮你生成的东西,但是引用的坐标对应的资源必须保障 Maven 仓库里面有才行,如果没有,还是要去下载的
总结
- 创建普通 Maven 工程
- 继承 spring-boot-starter-parent
- 添加依赖 spring-boot-starter-web
- 制作引导类 Application
到这里其实学习了 4 种创建 SpringBoot 工程的方式,其实本质是一样的,就是根据 SpringBoot 工程的文件格式要求,通过不同的方式生成或者手写得到对应的文件,效果完全一样。
教你一招:在 IDEA 中隐藏指定文件/文件夹
创建 SpringBoot 工程时,使用 SpringBoot 向导也好,阿里云也罢,其实都是为了一个目的,得到一个标准的SpringBoot 工程文件结构。这个时候就有新的问题出现了,标准的工程结构中包含了一些未知的文件夹,在开发的时候看起来特别别扭,这一节就来说说这些文件怎么处理。
处理方案无外乎两种,如果你对每一个文件 / 目录足够了解,没有用的完全可以删除掉,或者不删除,但是看着别扭,就设置文件为看不到就行了。删除不说了,直接 Delete 掉就好了,这一节说说如何隐藏指定的文件或文件夹信息。
既然是在 IDEA 下做隐藏功能,肯定隶属于 IDEA 的设置,设置方式如下。
步骤 ①:打开设置,【Files】→【Settings】

步骤 ②:打开文件类型设置界面,【Editor】→【File Types】→【Ignored Files and Folders】,忽略文件或文件夹显示

步骤 ③:添加你要隐藏的文件名称或文件夹名称,可以使用 * 号通配符,表示任意,设置完毕即可
到这里就做完了,其实就是 IDEA 的一个小功能
总结
IDEA 中隐藏指定文件或指定类型文件:
1. 【Files】→【Settings】
2. 【Editor】→【File Types】→【Ignored Files and Folders】
3. 输入要隐藏的名称,支持 * 号通配符
4. 回车确认添加
5、SpringBoot 简介
5.1、简介
入门案例做完了,这个时候回忆一下咱们之前说的 SpringBoot 的功能是什么还记得吗?加速 Spring 程序的开发,现在是否深有体会?再来看 SpringBoot 技术的设计初衷就很容易理解了。
SpringBoot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化 Spring 应用的初始搭建以及开发过程。
都简化了哪些东西呢?其实就是针对原始的 Spring 程序制作的两个方面进行了简化:
- Spring 程序缺点
- 依赖设置繁琐
- 以前写 Spring 程序,使用的技术都要自己一个一个的写,现在不需要了,如果做过原始 SpringMVC 程序的小伙伴应该知道,写 SpringMVC 程序,最基础的 spring-web 和 spring-webmvc 这两个坐标是必须的,就这还不包含你用 JSON 啊等等这些坐标,现在呢?一个坐标搞定
- 配置繁琐
- 以前写配置类或者配置文件,然后用什么东西就要自己写加载 Bean 这些东西,现在呢?什么都没写,照样能用
回顾
通过上面两个方面的定位,我们可以产生两个模糊的概念:
- SpringBoot 开发团队认为原始的 Spring 程序初始搭建的时候可能有些繁琐,这个过程是可以简化的,那原始的 Spring 程序初始搭建过程都包含哪些东西了呢?为什么觉得繁琐呢?最基本的 Spring 程序至少有一个配置文件或配置类,用来描述 Spring 的配置信息,莫非这个文件都可以不写?此外现在企业级开发使用 Spring 大部分情况下是做 Web 开发,如果做 Web 开发的话,还要在加载 Web 环境时加载时加载指定的 Spring 配置,这都是最基本的需求了,不写的话怎么知道加载哪个配置文件 / 配置类呢?那换了SpringBoot 技术以后呢,这些还要写吗?谜底稍后揭晓,先卖个关子
- SpringBoot 开发团队认为原始的 Spring 程序开发的过程也有些繁琐,这个过程仍然可以简化。开发过程无外乎使用什么技术,导入对应的 jar 包(或坐标)然后将这个技术的核心对象交给 Spring 容器管理,也就是配置成 Spring 容器管控的 Bean 就可以了。这都是基本操作啊,难道这些东西 SpringBoot 也能帮我们简化?
再来看看前面提出的两个问题,已经有答案了,都简化了,都不用写了,这就是 SpringBoot 给我们带来的好处。这些简化操作在 SpringBoot 中有专业的用语,也是 SpringBoot 程序的核心功能及优点:
- 起步依赖(简化依赖配置)
- 依赖配置的书写简化就是靠这个起步依赖达成的
- 自动配置(简化常用工程相关配置)
- 配置过于繁琐,使用自动配置就可以做响应的简化,但是内部还是很复杂的,后面具体展开说
- 辅助功能(内置服务器,……)
- 除了上面的功能,其实 SpringBoot 程序还有其他的一些优势,比如我们没有配置 Tomcat 服务器,但是能正常运行,这是 SpringBoot 程序的一个可以感知到的功能,也是 SpringBoot 的辅助功能之一。一个辅助功能都能做的这么 6,太牛了
下面结合入门程序来说说这些简化操作都在哪些方面进行体现的,一共分为 4 个方面
- parent
- starter
- 引导类
- 内嵌 Tomcat
5.2、parent
先看一个场景:两个工程用的相同的依赖坐标

为了简化,可写成一个 pom 文件

这种方式还不太科学,再把版本和坐标集中起来,配置成 Maven 的属性,形成一个新的 pom 文件。原 pom 文件再引用新 pom 文件的坐标,只做坐标管理不做版本管理

可以再扩充一下,列出所有的常用坐标

SpringBoot 关注到开发者在进行开发时,往往对依赖版本的选择具有固定的搭配格式,并且这些依赖版本的选择还不能乱搭配。比如 A 技术的 2.0 版与 B 技术的 3.5 版可以合作在一起,但是和 B 技术的 3.7 版合并使用时就有冲突。其实很多开发者都一直想做一件事情,就是将各种各样的技术配合使用的常见依赖版本进行收集整理,制作出了最合理的依赖版本配置方案,这样使用起来就方便多了。
SpringBoot 一看这种情况 so easy 啊,于是将所有的技术版本的常见使用方案都给开发者整理了出来,以后开发者使用时直接用它提供的版本方案,就不用担心冲突问题了,相当于 SpringBoot 做了无数个技术版本搭配的列表,这个技术搭配列表的名字叫做 parent。
parent 自身具有很多个版本,每个 parent 版本中包含有几百个其他技术的版本号,不同的 parent 间使用的各种技术的版本号有可能会发生变化。当开发者使用某些技术时,直接使用 SpringBoot 提供的 parent 就行了,由 parent 帮助开发者统一的进行各种技术的版本管理。
比如你现在要使用 Spring 配合 MyBatis 开发,没有 parent 之前怎么做呢?选个 Spring 的版本,再选个 MyBatis 的版本,再把这些技术使用时关联的其他技术的版本逐一确定下来。当你 Spring 的版本发生变化需要切换时,你的MyBatis 版本有可能也要跟着切换,关联技术呢?可能都要切换,而且切换后还可能出现问题。现在这一切工作都可以交给 parent 来做了。你无需关注这些技术间的版本冲突问题,你只需要关注你用什么技术就行了,冲突问题由 parent 负责处理。
有人可能会提出来,万一 parent 给我导入了一些我不想使用的依赖怎么办?记清楚,这一点很关键,parent 仅仅帮我们进行版本管理,它不负责帮你导入坐标,说白了用什么还是你自己定,只不过版本不需要你管理了。整体上来说,使用 parent 可以帮助开发者进行版本的统一管理。
关注:parent 定义出来以后,并不是直接使用的,仅仅给了开发者一个说明书,但是并没有实际使用,这个一定要确认清楚
那 SpringBoot 又是如何做到这一点的呢?可以查阅 SpringBoot 的配置源码,看到这些定义
- 项目中的 pom.xml 中继承了一个坐标
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
</parent>
- 打开后可以查阅到其中又继承了一个坐标
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.5.4</version>
</parent>
- 这个坐标中定义了两组信息。第一组是各式各样的依赖版本号属性,下面列出依赖版本属性的局部,可以看的出来,定义了若干个技术的依赖版本号
<properties>
<activemq.version>5.16.3</activemq.version>
<aspectj.version>1.9.7</aspectj.version>
<assertj.version>3.19.0</assertj.version>
<commons-codec.version>1.15</commons-codec.version>
<commons-dbcp2.version>2.8.0</commons-dbcp2.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<commons-pool.version>1.6</commons-pool.version>
<commons-pool2.version>2.9.0</commons-pool2.version>
<h2.version>1.4.200</h2.version>
<hibernate.version>5.4.32.Final</hibernate.version>
<hibernate-validator.version>6.2.0.Final</hibernate-validator.version>
<httpclient.version>4.5.13</httpclient.version>
<jackson-bom.version>2.12.4</jackson-bom.version>
<javax-jms.version>2.0.1</javax-jms.version>
<javax-json.version>1.1.4</javax-json.version>
<javax-websocket.version>1.1</javax-websocket.version>
<jetty-el.version>9.0.48</jetty-el.version>
<junit.version>4.13.2</junit.version>
</properties>
- 第二组是各式各样的的依赖坐标信息,可以看出依赖坐标定义中没有具体的依赖版本号,而是引用了第一组信息中定义的依赖版本属性值
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>{hibernate.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>{junit.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
关注:上面的依赖坐标定义是出现在 <dependencyManagement> 标签中的,其实是对引用坐标的依赖管理,并不是实际使用的坐标。因此当你的项目中继承了这组 parent 信息后,在不使用对应坐标的情况下,前面的这组定义是不会具体导入某个依赖的。
关注:因为在 Maven 中继承机会只有一次,上述继承的格式还可以切换成导入的形式进行,并且在阿里云的 starter 创建工程时就使用了此种形式
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
总结
- 开发 SpringBoot 程序要继承 spring-boot-starter-parent
- spring-boot-starter-parent 中定义了若干个依赖管理
- 继承 parent 模块可以避免多个依赖使用相同技术时出现依赖版本冲突
- 继承 parent 的形式也可以采用引入依赖的形式实现效果
思考
parent 中定义了若干个依赖版本管理,但是也没有使用,那这个设定也就不生效啊,究竟谁在使用这些定义呢?
5.3、starter
SpringBoot 关注到开发者在实际开发时,对于依赖坐标的使用往往都有一些固定的组合方式,比如使用 spring-webmvc 就一定要使用 spring-web。每次都要固定搭配着写,非常繁琐,而且格式固定,没有任何技术含量。
SpringBoot 一看这种情况,看来需要给开发者带来一些帮助了。安排,把所有的技术使用的固定搭配格式都给开发出来,以后你用某个技术,就不用一次写一堆依赖了,还容易写错,我给你做一个东西,代表一堆东西,开发者使用的时候,直接用我做好的这个东西就好了,对于这样的固定技术搭配,SpringBoot 给它起了个名字叫做 starter。starter 就是一个包含了若干个坐标定义的 pom 管理文件
starter 定义了使用某种技术时对于依赖的固定搭配格式,也是一种最佳解决方案,使用 starter 可以帮助开发者减少依赖配置
这个东西其实在入门案例里面已经使用过了,入门案例中的 Web 功能就是使用这种方式添加依赖的。可以查阅SpringBoot 的配置源码,看到这些定义
- 项目中的 pom.xml 定义了使用 SpringMVC 技术,但是并没有写 SpringMVC 的坐标,而是添加了一个名字中包含 starter 的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 在 spring-boot-starter-web 中又定义了若干个具体依赖的坐标
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.5.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.5.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
</dependencies>
之前提到过开发 SpringMVC 程序需要导入 spring-webmvc 的坐标和 spring 整合 web 开发的坐标,就是上面这组坐标中的最后两个了。
但是我们发现除了这两个还有其他的,比如第二个,叫做 spring-boot-starter-json。看名称就知道,这个是与 JSON 有关的坐标了,但是看名字发现和最后两个又不太一样,它的名字中也有 starter,打开看看里面有什么?
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.12.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.12.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<version>2.12.4</version>
<scope>compile</scope>
</dependency>
</dependencies>
我们可以发现,这个 starter 中又包含了若干个坐标,其实就是使用 SpringMVC 开发通常都会使用到 JSON,使用JSON 又离不开这里面定义的这些坐标,看来还真是方便,SpringBoot 把我们开发中使用的东西能用到的都给提前做好了。你仔细看完会发现,里面有一些你没用过的。的确会出现这种过量导入的可能性,没关系,可以通过 Maven 中的排除依赖剔除掉一部分。不过你不管它也没事,大不了就是过量导入呗。
到这里基本上得到了一个信息,使用 starter 可以帮开发者快速配置依赖关系。以前写依赖 3 个坐标的,现在写导入一个就搞定了,就是加速依赖配置的。
starter 与 parent 的区别
朦朦胧胧中感觉 starter 与 parent 好像都是帮助我们简化配置的,但是功能又不一样,梳理一下。
starter 是一个坐标中定了若干个坐标,以前写多个的,现在写一个,是用来减少依赖配置的书写量的
parent 是定义了几百个依赖版本号,以前写依赖需要自己手工控制版本,现在由 SpringBoot 统一管理,这样就不存在版本冲突了,是用来减少依赖冲突的。spring-boot-starter-parent 各版本间存在着诸多坐标版本不同。
实际开发应用方式
- 实际开发中如果需要用什么技术,先去找有没有这个技术对应的 starter
- 如果有对应的 starter,直接写 starter,而且无需指定版本,版本由 parent 提供
- 如果没有对应的 starter,手写坐标即可
- 实际开发中如果发现坐标出现了冲突现象,确认你要使用的可行的版本号,使用手工书写的方式添加对应依赖,覆盖 SpringBoot 提供给我们的配置管理
- 方式一:直接写坐标
- 方式二:覆盖
<properties>中定义的版本号,就是下面这堆东西了,哪个冲突了覆盖哪个就 OK 了
<properties>
<activemq.version>5.16.3</activemq.version>
<aspectj.version>1.9.7</aspectj.version>
<assertj.version>3.19.0</assertj.version>
<commons-codec.version>1.15</commons-codec.version>
<commons-dbcp2.version>2.8.0</commons-dbcp2.version>
<commons-lang3.version>3.12.0</commons-lang3.version>
<commons-pool.version>1.6</commons-pool.version>
<commons-pool2.version>2.9.0</commons-pool2.version>
<h2.version>1.4.200</h2.version>
<hibernate.version>5.4.32.Final</hibernate.version>
<hibernate-validator.version>6.2.0.Final</hibernate-validator.version>
<httpclient.version>4.5.13</httpclient.version>
<jackson-bom.version>2.12.4</jackson-bom.version>
<javax-jms.version>2.0.1</javax-jms.version>
<javax-json.version>1.1.4</javax-json.version>
<javax-websocket.version>1.1</javax-websocket.version>
<jetty-el.version>9.0.48</jetty-el.version>
<junit.version>4.13.2</junit.version>
</properties>
温馨提示
SpringBoot 官方给出了好多个 starter 的定义,方便我们使用,而且名称都是如下格式
命名规则:spring-boot-starter-技术名称
所以以后见了 spring-boot-starter-aaa 这样的名字,这就是 SpringBoot 官方给出的 starter 定义。那非官方定义的也有吗?有的,具体命名方式到整合章节再说
总结
- 开发 SpringBoot 程序需要导入坐标时通常导入对应的 starter
- 每个不同的 starter 根据功能不同,通常包含多个依赖坐标
- 使用 starter 可以实现快速配置的效果,达到简化配置的目的
5.4、引导类
配置说完了,我们发现 SpringBoot 确实帮助我们减少了很多配置工作,下面说一下程序是如何运行的。目前程序运行的入口就是 SpringBoot 工程创建时自带的那个类了,带有 main 方法的那个类,运行这个类就可以启动 SpringBoot 工程的运行
@SpringBootApplication
public class Springboot0101QuickstartApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot0101QuickstartApplication.class, args);
}
}
SpringBoot 本身是为了加速 Spring 程序的开发的,而 Spring 程序运行的基础是需要创建自己的 Spring 容器对象(IoC 容器)并将所有的对象交给 Spring 的容器管理,也就是一个一个的 Bean。那有了 SpringBoot 加速开发 Spring 程序,这个容器还在吗?这个疑问不用说,一定在。当前这个类运行后就会产生一个 Spring 容器对象,并且可以将这个对象保存起来,通过容器对象直接操作 Bean。
@SpringBootApplication
public class Springboot0101QuickstartApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Springboot0101QuickstartApplication.class, args);
BookController bean = ctx.getBean(BookController.class);
System.out.println("bean======>" + bean);
}
}
通过上述操作不难看出,其实 SpringBoot 程序启动还是创建了一个 Spring 容器对象。这个类在 SpringBoot 程序中是所有功能的入口,称这个类为引导类。
作为一个引导类最典型的特征就是当前类上方声明了一个注解 @SpringBootApplication
总结
- SpringBoot 工程提供引导类用来启动程序
- SpringBoot 工程启动后创建并初始化 Spring 容器,扫描引导类所在包加载 Bean
思考
程序现在已经运行了,通过引导类的 main 方法运行了起来。但是运行 Java 程序不应该是执行完就结束了吗?但是我们现在明显是启动了一个 Web 服务器啊,不然网页怎么能正常访问呢?这个服务器是在哪里写的呢?
5.5、内嵌 Tomcat
当前我们做的 SpringBoot 入门案例勾选了 Spring-web 的功能,并且导入了对应的 starter。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
SpringBoot 发现,既然你要做 Web 程序,肯定离不开使用 Web 服务器,这样吧,帮人帮到底,送佛送到西。我帮你搞一个 Web 服务器,你要愿意用的,直接使用就好了,干脆我再多给你几种选择,你随便切换。万一你不想用我给你提供的,也行,你可以自己搞。
由于这个功能不属于程序的主体功能,可用可不用,于是乎 SpringBoot 将其定位成辅助功能,别小看这么一个辅助功能,它可是帮我们开发者又减少了好多的设置性工作。
下面就围绕着这个内置的 Web 服务器,也可以说是内置的 Tomcat 服务器来研究几个问题
- 这个服务器在什么位置定义的
- 这个服务器是怎么运行的
- 这个服务器如果想换怎么换?虽然这个需求很垃圾,搞得开发者会好多 Web 服务器一样,用别人提供好的不香么?非要自己折腾
内嵌 Tomcat 定义位置
说到定义的位置,我们就想,如果我们不开发 Web 程序,用的着 Web 服务器吗?肯定用不着啊。那如果这个东西被加入到你的程序中,伴随着什么技术进来的呢?肯定是 Web 相关的功能啊,没错,就是前面导入的 Web 相关的 starter 做的这件事。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
打开查看 Web 的 starter 导入了哪些东西
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.5.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.5.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.5.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
<scope>compile</scope>
</dependency>
</dependencies>
第三个依赖就是这个 Tomcat 对应的东西了,居然也是一个 starter,再打开看看
<dependencies>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>1.3.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.52</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>tomcat-annotations-api</artifactId>
<groupId>org.apache.tomcat</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>9.0.52</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<version>9.0.52</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<artifactId>tomcat-annotations-api</artifactId>
<groupId>org.apache.tomcat</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
这里面有一个核心的坐标,tomcat-embed-core,叫做 Tomcat 内嵌核心。就是这个东西把 Tomcat 功能引入到了我们的程序中。目前解决了第一个问题,找到根儿了,谁把 Tomcat 引入到程序中的?spring-boot-starter-web 中的spring-boot-starter-tomcat 做的。之所以你感觉很奇妙的原因就是,这个东西是默认加入到程序中了,所以感觉很神奇,居然什么都不做,就有了 Web 服务器对应的功能,再来说第二个问题,这个服务器是怎么运行的
内嵌 Tomcat 运行原理
Tomcat 服务器是一款软件,而且是一款使用 Java 语言开发的软件,熟悉的小伙伴可能有印象,Tomcat 安装目录中保存有好多个 jar。
下面的问题来了,既然是使用 Java 语言开发的,运行的时候肯定符合 Java 程序运行的原理,Java 程序运行靠的是什么?对象呀,一切皆对象,万物皆对象。那 Tomcat 运行起来呢?也是对象。
如果是对象,那 Spring 容器是用来管理对象的,这个对象能不能交给 Spring 容器管理呢?把吗去掉,是个对象都可以交给 Spring 容器管理,行了,这下通了。Tomcat 服务器运行其实是以对象的形式在 Spring 容器中运行的,怪不得我们没有安装这个 Tomcat,而且还能用。闹了半天这东西最后是以一个对象的形式存在,保存在 Spring 容器中悄悄运行的。具体运行的是什么呢?其实就是上前面提到的那个 Tomcat 内嵌核心
<dependencies>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>9.0.52</version>
<scope>compile</scope>
</dependency>
</dependencies>
那既然是个对象,如果把这个对象从 Spring 容器中去掉是不是就没有 Web 服务器的功能呢?是这样的,通过依赖排除可以去掉这个 Web 服务器功能
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
上面对 web-starter 做了一个操作,使用 Maven 的排除依赖去掉了使用 Tomcat 的 starter。这下好了,容器中肯定没有这个对象了,重新启动程序可以观察到程序运行了,但是并没有像之前那样运行后会等着用户发请求,而是直接停掉了,就是这个原因了。
更换内嵌 Tomcat
那根据上面的操作我们思考是否可以换个服务器呢?必须的嘛。根据 SpringBoot 的工作机制,用什么技术,加入什么依赖就行了。SpringBoot 提供了 3 款内置的服务器
- Tomcat(默认):apache 出品,粉丝多,应用面广,负载了若干较重的组件
-
jetty:更轻量级,但负载性能远不及 Tomcat
-
undertow:负载性能勉强跑赢 Tomcat
想用哪个,加个坐标就 OK。前提是把 Tomcat 排除掉,因为 Tomcat 是默认加载的。
<!--使用maven依赖管理变更起步依赖项-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!--web起步依赖环境中,排除Tomcat起步依赖-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--添加Jetty起步依赖,版本由SpringBoot的starter控制-->
<!--Jetty比Tomcat更轻量级,相较于Tomcat可扩展性更强,谷歌应用引擎(GAE)已经全面切换为Jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
</dependencies>
现在就已经成功替换了 Web 服务器,核心思想就是用什么加入对应坐标就可以了。如果有starter,优先使用 starter
总结
- 内嵌 Tomcat 服务器是 SpringBoot 辅助功能之一
- 内嵌 Tomcat 工作原理是将 Tomcat 服务器作为对象运行,并将该对象交给 Spring 容器管理
- 变更内嵌服务器思想是去除现有服务器,添加全新的服务器
到这里第一章快速上手 SpringBoot 就结束了,这一章我们学习了两大块知识
- 使用了 4 种方式制作了 SpringBoot 的入门程序,不管是哪一种,其实内部都是一模一样的
- 学习了入门程序的工作流程,知道什么是 parent,什么是 starter,这两个东西是怎么配合工作的,以及我们的程序为什么启动起来是一个 Tomcat 服务器等等
第一章到这里就结束了,再往下学习就要去基于会创建 SpringBoot 工程的基础上,研究 SpringBoot 工程的具体细节了。