引言
現(xiàn)在各大技術(shù)社區(qū) Spring Boot 的文章越來(lái)越多,Spring Boot 相關(guān)的圖文、視頻教程越來(lái)越多,使用 Spring Boot 的互聯(lián)網(wǎng)公司也越來(lái)越多; Java 程序員現(xiàn)在出去面試, Spring Boot 已經(jīng)成了必問(wèn)的內(nèi)容。
一切都在證明,Spring Boot 已經(jīng)成為了 Java 程序員必備的技能。并且可以預(yù)見的是未來(lái) Spring Boot 的發(fā)展還會(huì)更好。
所以對(duì)Java程序員來(lái)說(shuō)其中不乏說(shuō)對(duì) Spring Boot 非常熟悉的,然后當(dāng)問(wèn)到一些 Spring Boot 核心功能和原理的時(shí)候,沒(méi)人能說(shuō)得上來(lái),或者說(shuō)不到點(diǎn)上,可以說(shuō)一個(gè)問(wèn)題就問(wèn)趴下了!(問(wèn)題:你能講下為什么我們要用 Spring Boot 嗎?)
相信我,上面這些類似的問(wèn)題,90%有經(jīng)驗(yàn)的Java程序員超都曾遇見過(guò)!但很少有系統(tǒng)化的回答。
因此,總結(jié)了這份Spring Boot核心知識(shí)點(diǎn)實(shí)戰(zhàn)教程,通過(guò)這份教程,帶你梳理Spring Boot 技術(shù)體系。
文末有彩蛋~

Spring Boot2教程
在Spring Boot項(xiàng)目中,正常來(lái)說(shuō)是不存在XML配置,這是因?yàn)镾pring Boot不推薦使用 XML ,注意,并非不支持,Spring Boot 推薦開發(fā)者使用 Java 配置來(lái)搭建框架,Spring Boot 中,大量的自動(dòng)化配置都是通過(guò) Java 配置來(lái)實(shí)現(xiàn)的,這一套實(shí)現(xiàn)方案,我們也可以自己做,即自己也可以使用純 Java 來(lái)搭建一個(gè) SSM 環(huán)境,即在項(xiàng)目中,不存在任何 XML 配置,包括 web.xml 。
環(huán)境要求:
使用純 Java 來(lái)搭建 SSM 環(huán)境,要求 Tomcat 的版本必須在 7 以上。

1、創(chuàng)建工程
創(chuàng)建一個(gè)普通的 Maven工程(注意,這里可以不必創(chuàng)建Web工程),并添加SpringMVC的依賴,同時(shí),這里環(huán)境的搭建需要用到 Servlet ,所以我們還需要引入 Servlet 的依賴(一定不能使用低版本的Servlet),最終的 pom.xml 文件如下:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
2 、添加 Spring 配置
工程創(chuàng)建成功之后,首先添加 Spring 的配置文件,如下:
@Configuration
@ComponentScan(basePackages = "org.javaboy", useDefaultFilters = true,
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes =
Controller.class)})
public class SpringConfig {
}
關(guān)于這個(gè)配置,我說(shuō)如下幾點(diǎn):
@Configuration 注解表示這是一個(gè)配置類,在我們這里,這個(gè)配置的作用類似于applicationContext.xml
@ComponentScan 注解表示配置包掃描,里邊的屬性和 xml 配置中的屬性都是一一對(duì)應(yīng)的,useDefaultFilters 表示使用默認(rèn)的過(guò)濾器,然后又除去 Controller 注解,即在 Spring 容器中掃描除了 Controller 之外的其他所有 Bean 。
3、 添加 SpringMVC 配置
接下來(lái)再來(lái)創(chuàng)建 springmvc 的配置文件:
@Configuration
@ComponentScan(basePackages = "org.javaboy",useDefaultFilters =
false,includeFilters = {@ComponentScan.Filter(type =
FilterType.ANNOTATION,classes = Controller.class)})
public class SpringMVCConfig {
}
注意,如果不需要在SpringMVC中添加其他的額外配置,這樣就可以了。即視圖解析器、JSON解析、文件上傳......等等,如果都不需要配置的話,這樣就可以了。

4、配置 web.xml
此時(shí),我們并沒(méi) web.xml 文件,這時(shí),我們可以使用Java代碼去代替 web.xml 文件,這里會(huì)用到WebApplicationInitializer ,具體定義如下:
public class WebInit implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException
{
//首先來(lái)加載 SpringMVC 的配置文件
AnnotationConfigWebApplicationContext ctx = new
AnnotationConfigWebApplicationContext();
ctx.register(SpringMVCConfig.class);
// 添加 DispatcherServlet
ServletRegistration.Dynamic springmvc =
servletContext.addServlet("springmvc", new DispatcherServlet(ctx));
// 給 DispatcherServlet 添加路徑映射
springmvc.addMapping("/");
// 給 DispatcherServlet 添加啟動(dòng)時(shí)機(jī)
springmvc.setLoadOnStartup(1);
}
}
WebInit 的作用類似于 web.xml,這個(gè)類需要實(shí)現(xiàn) WebApplicationInitializer 接口,并實(shí)現(xiàn)接口中的方法,當(dāng)項(xiàng)目啟動(dòng)時(shí),onStartup 方法會(huì)被自動(dòng)執(zhí)行,我們可以在這個(gè)方法中做一些項(xiàng)目初始化操作,例如加載 SpringMVC 容器,添加過(guò)濾器,添加 Listener、添加 Servlet 等。
注意:
由于我們?cè)赪ebInit中只是添加了SpringMVC的配置,這樣項(xiàng)目在啟動(dòng)時(shí)只會(huì)去加載SpringMVC容器,而不會(huì)去加載 Spring 容器,如果一定要加載 Spring 容器,需要我們修改 SpringMVC 的配置,在SpringMVC 配置的包掃描中也去掃描 @Configuration 注解,進(jìn)而加載 Spring 容器,還有一種方案可以解決這個(gè)問(wèn)題,就是直接在項(xiàng)目中舍棄 Spring 配置,直接將所有配置放到 SpringMVC 的配置中來(lái)完成,這個(gè)在 SSM 整合時(shí)是沒(méi)有問(wèn)題的,在實(shí)際開發(fā)中,較多采用第二種方案,第二種方案,SpringMVC 的配置如下:
@Configuration
@ComponentScan(basePackages = "org.javaboy")
public class SpringMVCConfig {
}
這種方案中,所有的注解都在 SpringMVC 中掃描,采用這種方案的話,則 Spring 的配置文件就可以刪除了。
5、測(cè)試
最后,添加一個(gè) HelloController ,然后啟動(dòng)項(xiàng)目進(jìn)行測(cè)試:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
啟動(dòng)項(xiàng)目,訪問(wèn)接口,結(jié)果如下:

Spring Boot全局異常處理
在Spring Boot項(xiàng)目中 ,異常統(tǒng)一處理,可以使用Spring中@ControllerAdvice來(lái)統(tǒng)一處理,也可以自己來(lái)定義異常處理方案。Spring Boot 中,對(duì)異常的處理有一些默認(rèn)的策略,我們分別來(lái)看。
默認(rèn)情況下,Spring Boot 中的異常頁(yè)面 是這樣的:

我們從這個(gè)異常提示中,也能看出來(lái),之所以用戶看到這個(gè)頁(yè)面,是因?yàn)殚_發(fā)者沒(méi)有明確提供一個(gè)/error 路徑,如果開發(fā)者提供了 /error 路徑 ,這個(gè)頁(yè)面就不會(huì)展示出來(lái),不過(guò)在 Spring Boot 中,提供/error 路徑實(shí)際上是下下策,Spring Boot本身在處理異常時(shí),也是當(dāng)所有條件都不滿足時(shí),才會(huì)去找 /error 路徑。那么我們就先來(lái)看看,在 Spring Boot 中,如何自定義 error 頁(yè)面,整體上來(lái)說(shuō),可以分為兩種,一種是靜態(tài)頁(yè)面,另一種是動(dòng)態(tài)頁(yè)面。
靜態(tài)異常頁(yè)面
自定義靜態(tài)異常頁(yè)面,又分為兩種,第一種 是使用HTTP響應(yīng)碼來(lái)命名頁(yè)面,例如404.html、405.html、500.html ....,另一種就是直接定義一個(gè) 4xx.html,表示400-499 的狀態(tài)都顯示這個(gè)異常頁(yè)面,5xx.html 表示 500-599 的狀態(tài)顯示這個(gè)異常頁(yè)面。
默認(rèn)是在 classpath:/static/error/ 路徑下定義相關(guān)頁(yè)面:

此時(shí),啟動(dòng)項(xiàng)目,如果項(xiàng)目拋出 500 請(qǐng)求錯(cuò)誤,就會(huì)自動(dòng)展示 500.html 這個(gè)頁(yè)面,發(fā)生 404 就會(huì)展示404.html 頁(yè)面。如果異常展示頁(yè)面既存在 5xx.html,也存在 500.html ,此時(shí),發(fā)生500異常時(shí),優(yōu)先展示 500.html 頁(yè)面。
動(dòng)態(tài)異常頁(yè)面
動(dòng)態(tài)的異常頁(yè)面定義方式和靜態(tài)的基本 一致,可以采用的頁(yè)面模板有 jsp、freemarker、thymeleaf。
動(dòng)態(tài)異常頁(yè)面,也支持 404.html 或者 4xx.html ,但是一般來(lái)說(shuō),由于動(dòng)態(tài)異常頁(yè)面可以直接展示異常詳細(xì)信息,所以就沒(méi)有必要挨個(gè)枚舉錯(cuò)誤了 ,直接定義 4xx.html(這里使用thymeleaf模板)或者5xx.html 即可。
注意,動(dòng)態(tài)頁(yè)面模板,不需要開發(fā)者自己去定義控制器,直接定義異常頁(yè)面即可 ,Spring Boot 中自帶的異常處理器會(huì)自動(dòng)查找到異常頁(yè)面。
頁(yè)面定義如下:

頁(yè)面內(nèi)容如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>5xx</h1>
<table border="1">
<tr>
<td>path</td>
<td th:text="${path}"></td>
</tr>
<tr>
<td>error</td>
<td th:text="${error}"></td>
</tr>
<tr>
<td>message</td>
<td th:text="${message}"></td>
</tr>
<tr>
<td>timestamp</td>
<td th:text="${timestamp}"></td>
</tr>
<tr>
<td>status</td>
<td th:text="${status}"></td>
</tr>
</table>
</body>
</html>
默認(rèn)情況下,完整的異常信息就是這5條,展示 效果如下 :

如果動(dòng)態(tài)頁(yè)面和靜態(tài)頁(yè)面同時(shí)定義了異常處理頁(yè)面,例如 classpath:/static/error/404.html 和classpath:/templates/error/404.html 同時(shí)存在時(shí),默認(rèn)使用動(dòng)態(tài)頁(yè)面。即完整的錯(cuò)誤頁(yè)面查找
方式應(yīng)該是這樣:
發(fā)生了 500 錯(cuò)誤-->查找動(dòng)態(tài) 500.html 頁(yè)面-->查找靜態(tài) 500.html --> 查找動(dòng)態(tài) 5xx.html-->查找靜態(tài)5xx.html。

自定義異常數(shù)據(jù)
默認(rèn)情況下,在 Spring Boot 中,所有的異常數(shù)據(jù)其實(shí)就是上文所展示出來(lái)的 5 條數(shù)據(jù),這 5 條數(shù)據(jù)定義在 org.springframework.boot.web.reactive.error.DefaultErrorAttributes 類中,具體定義在 getErrorAttributes 方法中 :
public Map<String, Object> getErrorAttributes(ServerRequest request,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<>();
errorAttributes.put("timestamp", new Date());
errorAttributes.put("path", request.path());
Throwable error = getError(request);
HttpStatus errorStatus = determineHttpStatus(error);
errorAttributes.put("status", errorStatus.value());
errorAttributes.put("error", errorStatus.getReasonPhrase());
errorAttributes.put("message", determineMessage(error));
handleException(errorAttributes, determineException(error),
includeStackTrace);
return errorAttributes;
}
DefaultErrorAttributes 類本身則是在
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration 異常自動(dòng)配置類中定義的,如果開發(fā)者沒(méi)有自己提供一個(gè) ErrorAttributes 的實(shí)例的話,那么 Spring Boot 將自動(dòng)提供一個(gè) ErrorAttributes 的實(shí)例,也就是 DefaultErrorAttributes 。
基于此 ,開發(fā)者自定義 ErrorAttributes 有兩種方式 :
直接實(shí)現(xiàn) ErrorAttributes 接口
繼承 DefaultErrorAttributes(推薦),因?yàn)?DefaultErrorAttributes 中對(duì)異常數(shù)據(jù)的處理已經(jīng)完成,開發(fā)者可以直接使用。
具體定義如下:
@Component
public class MyErrorAttributes extends DefaultErrorAttributes {
@Override
public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean
includeStackTrace) {
Map<String, Object> map = super.getErrorAttributes(webRequest,
includeStackTrace);
if ((Integer)map.get("status") == 500) {
map.put("message", "服務(wù)器內(nèi)部錯(cuò)誤!");
}
return map;
}
}
定義好的 ErrorAttributes 一定要注冊(cè)成一個(gè) Bean ,這樣,Spring Boot 就不會(huì)使用默認(rèn)的DefaultErrorAttributes 了,運(yùn)行效果如下圖:

自定義異常視圖
異常視圖默認(rèn)就是前面所說(shuō)的靜態(tài)或者動(dòng)態(tài)頁(yè)面,這個(gè)也是可以自定義的,首先 ,默認(rèn)的異常視圖加載邏輯在 org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController 類的errorHtml 方法中,這個(gè)方法用來(lái)返回異常頁(yè)面+數(shù)據(jù),還有另外一個(gè) error 方法,這個(gè)方法用來(lái)返回異常數(shù)據(jù)(如果是 ajax 請(qǐng)求,則該方法會(huì)被觸發(fā))。
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model =
Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request,
MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status,
model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error",
model);
}
在該方法中 ,首先會(huì)通過(guò) getErrorAttributes 方法去獲取異常數(shù)據(jù)(實(shí)際上會(huì)調(diào)用到 ErrorAttributes的實(shí)例 的 getErrorAttributes 方法),然后調(diào)用 resolveErrorView 去創(chuàng)建一個(gè) ModelAndView ,如果這里創(chuàng)建失敗,那么用戶將會(huì)看到默認(rèn)的錯(cuò)誤提示頁(yè)面。
正常情況下, resolveErrorView 方法會(huì)來(lái)到 DefaultErrorViewResolver 類的 resolveErrorView 方法中:
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus
status,
Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status.value()),
model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()),
model);
}
return modelAndView;
}
在這里,首先以異常響應(yīng)碼作為視圖名分別去查找動(dòng)態(tài)頁(yè)面和靜態(tài)頁(yè)面,如果沒(méi)有查找到,則再以 4xx或者 5xx 作為視圖名再去分別查找動(dòng)態(tài)或者靜態(tài)頁(yè)面。
要自定義異常視圖解析,也很容易 ,由于 DefaultErrorViewResolver 是在ErrorMvcAutoConfiguration 類中提供的實(shí)例,即開發(fā)者沒(méi)有提供相關(guān)實(shí)例時(shí),會(huì)使用默認(rèn)的DefaultErrorViewResolver ,開發(fā)者提供了自己的 ErrorViewResolver 實(shí)例后,默認(rèn)的配置就會(huì)失效,因此,自定義異常視圖,只需要提供 一個(gè) ErrorViewResolver 的實(shí)例即可:
@Component
public class MyErrorViewResolver extends DefaultErrorViewResolver {
public MyErrorViewResolver(ApplicationContext applicationContext,
ResourceProperties resourceProperties) {
super(applicationContext, resourceProperties);
}
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus
status, Map<String, Object> model) {
return new ModelAndView("/aaa/123", model);
}
}
實(shí)際上,開發(fā)者也可以在這里定義異常數(shù)據(jù)(直接在 resolveErrorView 方法重新定義一個(gè) model ,將參數(shù)中的model 數(shù)據(jù)拷貝過(guò)去并修改,注意參數(shù)中的 model 類型為 UnmodifiableMap,即不可以直接修改),而不需要自定義 MyErrorAttributes。定義完成后,提供一個(gè)名為 123 的視圖,如下圖:

如此之后,錯(cuò)誤試圖就算定義成功了。
總結(jié)
實(shí)際上也可以自定義異??刂破?BasicErrorController ,不過(guò)我覺(jué)得這樣太大動(dòng)干戈了,沒(méi)必要,前面幾種方式已經(jīng)可以滿足我們的大部分開發(fā)需求了。如果是前后端分離架構(gòu),異常處理還有其他一些處理方案,這個(gè)以后和大家聊。

篇幅有限,其他內(nèi)容就不在這里一一展示了,這份Spring Boot實(shí)戰(zhàn)教程已整理成一份PDF文檔,共有200多頁(yè)。
關(guān)注公眾號(hào):程序零世界,回復(fù) 666 獲取這份整理好的Spring Boot實(shí)戰(zhàn)教程。

最后
歡迎大家一起交流,喜歡文章記得點(diǎn)ge 贊喲,感謝支持!
