Spring 和 Spring Boot 的區別很多新手容易搞混,從這道簡單的面試題也可以很輕易試探出你的 Java 基礎功底,如果連這個問題都答不上來的話,通常就沒有什么下文了,因為這已經是用人單位對面試者的最低要求了,所以本課時我們就來看一下二者的區別,以及 Spring Boot 的特性。
我們本課時的面試題是,Spring 和 Spring Boot 有什么區別?Spring Boot 的優點有哪些?
#### 典型回答
作為 Java 開發人員對 Spring 框架都很熟悉,Spring 為 Java 程序提供了全面的基礎架構支持,包含了很多非常實用的功能,如 Spring JDBC、Spring AOP、Spring ORM、Spring Test 等,這些模塊的出現,大大的縮短了應用程序的開發時間,同時提高了應用開發的效率。
Spring Boot 本質上是 Spring 框架的延伸和擴展,它的誕生是為了簡化 Spring 框架初始搭建以及開發的過程,使用它可以不再依賴 Spring 應用程序中的 XML 配置,為更快、更高效的開發 Spring 提供更加有力的支持。Spring Boot 具體的特性如下。
* [ ] Spring Boot 特性一:更快速的構建能力
Spring Boot 提供了更多的 Starters 用于快速構建業務框架,Starters 可以理解為啟動器,它包含了一系列可以集成到應用里面的依賴包,你可以一站式集成 Spring 及其他技術,而不需要到處找依賴包。
例如在 Spring 中如果要創建 Web 應用程序的最小依賴項為:
```
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>xxx</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>xxx</version>
</dependency>
```
而 Spring Boot 只需要一個依賴項就可以來啟動和運行 Web 應用程序,如下所示:
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
```
當我們添加了 Starter 模塊支持之后,在項目的構建期,它就會把所有其他依賴項將自動添加到項目中。
這樣的例子還有很多,比如測試庫,如果是 Spring 項目我們通常要添加 Spring Test、JUnit、Hamcrest 和 Mockito 庫;而如果是 Spring Boot 項目的話,只需要添加 spring-boot-starter-test 即可,它會自動幫我們把其他的依賴項添加到項目中。
常見的 Starters 有以下幾個:
```
spring-boot-starter-test
spring-boot-starter-web
spring-boot-starter-data-jpa
spring-boot-starter-thymeleaf
```
[點擊這里](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-starter)訪問文檔,查看完整的 Starters 列表。
* [ ] Spring Boot 特性二:起步依賴
Spring Boot 提供了起步依賴,也就是在創建 Spring Boot 時可以直接勾選依賴模塊,這樣在項目初始化時就會把相關依賴直接添加到項目中,大大縮短了查詢并添加依賴的時間,如下圖所示:

* [ ] Spring Boot 特性三:內嵌容器支持
Spring Boot 內嵌了 Tomcat、Jetty、Undertow 三種容器,其默認嵌入的容器是 Tomcat,這個在我們啟動 Spring Boot 項目的時候,在控制臺上就能看到,具體信息如下:
```
o.s.b.w.embedded.tomcat.TomcatWebServer :Tomcat started on port(s): 8080 (http) with context path ''
```
可以看出 Spring Boot 默認使用的是 Tomcat 容器啟動的。
我們可以通過修改 pom.xml 來移除內嵌的 Tomcat 更換為其他的容器,比如更換為 Jetty 容器,配置如下:
```
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 移處 Tomcat -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 移處 jetty 容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
```
當我們添加完成之后,再重新生成 pom.xml 文件,然后再啟動 Spring Boot 項目容器信息就變了,如下所示:
```
o.e.jetty.server.AbstractConnector: Started ServerConnector@53f9009d{HTTP/1.1, (http/1.1)}{0.0.0.0:8080}
o.s.b.web.embedded.jetty.JettyWebServer
```
可以看出 Spring Boot 使用了我們指定的 Jetty 容器啟動了。
* [ ] Spring Boot 特性四:Actuator 監控
Spring Boot 自帶了 Actuator 監控功能,主要用于提供對應用程序監控,以及控制的能力,比如監控應用程序的運行狀況,或者內存、線程池、Http 請求統計等,同時還提供了關閉應用程序等功能。
Actuator 提供了 19 個接口,接口請求地址和代表含義如下表所示:

#### 考點分析
很多人都知道 Spring Boot 是基于 Spring 的,使用它可以更加快速高效的構建 Spring,然而當面試官問到 Spring Boot 是如何高效構建 Spring 時,可能大部分人回答不上來了,上面講解的 Spring Boot 四大特性基本涵蓋了此問題的答案。如果面試官繼續追問更深的細節的話,可能會問到關于 Spring Boot 執行的源碼細節,比如 Spring Boot 的啟動流程是怎么樣的?
#### 知識擴展
* [ ] Spring Boot 啟動源碼分析
我們知道 Spring Boot 程序的入口是 SpringApplication.run(Application.class, args) 方法,那么就從 run() 方法開始分析吧,它的源碼如下:
```
public ConfigurableApplicationContext run(String... args) {
// 1.創建并啟動計時監控類
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 2.聲明應用上下文對象和異常報告集合
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
// 3.設置系統屬性 headless 的值
this.configureHeadlessProperty();
// 4.創建所有 Spring 運行監聽器并發布應用啟動事件
SpringApplicationRunListeners listeners = this.getRunListeners(args);
listeners.starting();
Collection exceptionReporters;
try {
// 5.處理 args 參數
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 6.準備環境
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 7.創建 Banner 的打印類
Banner printedBanner = this.printBanner(environment);
// 8.創建應用上下文
context = this.createApplicationContext();
// 9.實例化異常報告器
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 10.準備應用上下文
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 11.刷新應用上下文
this.refreshContext(context);
// 12.應用上下文刷新之后的事件的處理
this.afterRefresh(context, applicationArguments);
// 13.停止計時監控類
stopWatch.stop();
// 14.輸出日志記錄執行主類名、時間信息
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 15.發布應用上下文啟動完成事件
listeners.started(context);
// 16.執行所有 Runner 運行器
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 17.發布應用上下文就緒事件
listeners.running(context);
// 18.返回應用上下文對象
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
```
從以上源碼可以看出 Spring Boot 的啟動總共分為以下 18 個步驟。
* [ ] Spring Boot 的啟動流程
* 1.創建并啟動計時監控類
此計時器是為了監控并記錄 Spring Boot 應用啟動的時間的,它會記錄當前任務的名稱,然后開啟計時器。
* 2.聲明應用上下文對象和異常報告集合
此過程聲明了應用上下文對象和一個異常報告的 ArrayList 集合。
* 3.設置系統屬性 headless 的值
設置 Java.awt.headless = true,其中 awt(Abstract Window Toolkit)的含義是抽象窗口工具集。設置為 true 表示運行一個 headless 服務器,可以用它來作一些簡單的圖像處理。
* 4.創建所有 Spring 運行監聽器并發布應用啟動事件
此過程用于獲取配置的監聽器名稱并實例化所有的類。
* 5.初始化默認應用的參數類
也就是說聲明并創建一個應用參數對象。
* 6.準備環境
創建配置并且綁定環境(通過 property sources 和 profiles 等配置文件)。
* 7.創建 Banner 的打印類
Spring Boot 啟動時會打印 Banner 圖片,如下圖所示:

此 banner 信息是在 SpringBootBanner 類中定義的,我們可以通過實現 Banner 接口來自定義 banner 信息,然后通過代碼 setBanner() 方法設置 Spring Boot 項目使用自己自定義 Banner 信息,或者是在 resources 下添加一個 banner.txt,把 banner 信息添加到此文件中,就可以實現自定義 banner 的功能了。
* 8.創建應用上下文
根據不同的應用類型來創建不同的 ApplicationContext 上下文對象。
* 9.實例化異常報告器
它調用的是 getSpringFactoriesInstances() 方法來獲取配置異常類的名稱,并實例化所有的異常處理類。
* 10.準備應用上下文
此方法的主要作用是把上面已經創建好的對象,傳遞給 prepareContext 來準備上下文,例如將環境變量 environment 對象綁定到上下文中、配置 bean 生成器以及資源加載器、記錄啟動日志等操作。
* 11.刷新應用上下文
此方法用于解析配置文件,加載 bean 對象,并且啟動內置的 web 容器等操作。
* 12.應用上下文刷新之后的事件處理
這個方法的源碼是空的,可以做一些自定義的后置處理操作。
* 13.停止計時監控類
停止此過程第一步中的程序計時器,并統計任務的執行信息。
* 14.輸出日志信息
把相關的記錄信息,如類名、時間等信息進行控制臺輸出。
* 15.發布應用上下文啟動完成事件
觸發所有 SpringApplicationRunListener 監聽器的 started 事件方法。
* 16.執行所有 Runner 運行器
執行所有的 ApplicationRunner 和 CommandLineRunner 運行器。
* 17.發布應用上下文就緒事件
觸發所有的 SpringApplicationRunListener 監聽器的 running 事件。
* 18.返回應用上下文對象
到此為止 Spring Boot 的啟動程序就結束了,我們就可以正常來使用 Spring Boot 框架了。
#### 小結
本課時首先講了 Spring 和 Spring Boot 的區別,Spring Boot 本質上是 Spring 的延伸,它是基于 Spring 的,它為快速構建和開發 Spring 提供了有力的支撐;接著介紹了 Spring Boot 的四大特性:更快速的構建能力、起步依賴、內嵌容器支持、Actuator 監控支持等,最后 還介紹了 Spring Boot 啟動的 18 個步驟。
#### 課后問答
* 1、磊哥,springBoot 啟動的時候比Spring多出來的,有哪些是可以擴展的接口?
講師回復: springboot 本質上還是 spring,所以他們基本是相同的,只是使用 springboot 有入門依賴和內置容器,使用更方便了。
- 前言
- 開篇詞
- 開篇詞:大廠技術面試“潛規則”
- 模塊一:Java 基礎
- 第01講:String 的特點是什么?它有哪些重要的方法?
- 第02講:HashMap 底層實現原理是什么?JDK8 做了哪些優化?
- 第03講:線程的狀態有哪些?它是如何工作的?
- 第04講:詳解 ThreadPoolExecutor 的參數含義及源碼執行流程?
- 第05講:synchronized 和 ReentrantLock 的實現原理是什么?它們有什么區別?
- 第06講:談談你對鎖的理解?如何手動模擬一個死鎖?
- 第07講:深克隆和淺克隆有什么區別?它的實現方式有哪些?
- 第08講:動態代理是如何實現的?JDK Proxy 和 CGLib 有什么區別?
- 第09講:如何實現本地緩存和分布式緩存?
- 第10講:如何手寫一個消息隊列和延遲消息隊列?
- 模塊二:熱門框架
- 第11講:底層源碼分析 Spring 的核心功能和執行流程?(上)
- 第12講:底層源碼分析 Spring 的核心功能和執行流程?(下)
- 第13講:MyBatis 使用了哪些設計模式?在源碼中是如何體現的?
- 第14講:SpringBoot 有哪些優點?它和 Spring 有什么區別?
- 第15講:MQ 有什么作用?你都用過哪些 MQ 中間件?
- 模塊三:數據庫相關
- 第16講:MySQL 的運行機制是什么?它有哪些引擎?
- 第17講:MySQL 的優化方案有哪些?
- 第18講:關系型數據和文檔型數據庫有什么區別?
- 第19講:Redis 的過期策略和內存淘汰機制有什么區別?
- 第20講:Redis 怎樣實現的分布式鎖?
- 第21講:Redis 中如何實現的消息隊列?實現的方式有幾種?
- 第22講:Redis 是如何實現高可用的?
- 模塊四:Java 進階
- 第23講:說一下 JVM 的內存布局和運行原理?
- 第24講:垃圾回收算法有哪些?
- 第25講:你用過哪些垃圾回收器?它們有什么區別?
- 第26講:生產環境如何排除和優化 JVM?
- 第27講:單例的實現方式有幾種?它們有什么優缺點?
- 第28講:你知道哪些設計模式?分別對應的應用場景有哪些?
- 第29講:紅黑樹和平衡二叉樹有什么區別?
- 第30講:你知道哪些算法?講一下它的內部實現過程?
- 模塊五:加分項
- 第31講:如何保證接口的冪等性?常見的實現方案有哪些?
- 第32講:TCP 為什么需要三次握手?
- 第33講:Nginx 的負載均衡模式有哪些?它的實現原理是什么?
- 第34講:Docker 有什么優點?使用時需要注意什么問題?
- 彩蛋
- 彩蛋:如何提高面試成功率?