互聯網的寒冬下各大一線互聯網公司還在用SpringBoot這是為什麼?

引言

現在各大技術社區 Spring Boot 的文章越來越多,Spring Boot 相關的圖文、視頻教程越來越多,使用 Spring Boot 的互聯網公司也越來越多; Java 程序員現在出去面試, Spring Boot 已經成了必問的內容。

一切都在證明,Spring Boot 已經成為了 Java 程序員必備的技能。並且可以預見的是未來 Spring Boot 的發展還會更好。

所以對Java程序員來說其中不乏說對 Spring Boot 非常熟悉的,然後當問到一些 Spring Boot 核心功能和原理的時候,沒人能說得上來,或者說不到點上,可以說一個問題就問趴下了!(問題:你能講下為什麼我們要用 Spring Boot 嗎?)

相信我,上面這些類似的問題,90%有經驗的Java程序員超都曾遇見過!但很少有系統化的回答。

因此,總結了這份Spring Boot核心知識點實戰教程,通過這份教程,帶你梳理Spring Boot 技術體系。

文末有彩蛋~
在這裡插入圖片描述

Spring Boot2教程
在Spring Boot項目中,正常來說是不存在XML配置,這是因為Spring Boot不推薦使用 XML ,注意,並非不支持,Spring Boot 推薦開發者使用 Java 配置來搭建框架,Spring Boot 中,大量的自動化配置都是通過 Java 配置來實現的,這一套實現方案,我們也可以自己做,即自己也可以使用純 Java 來搭建一個 SSM 環境,即在項目中,不存在任何 XML 配置,包括 web.xml 。

環境要求:

使用純 Java 來搭建 SSM 環境,要求 Tomcat 的版本必須在 7 以上。

在這裡插入圖片描述

1、創建工程

創建一個普通的 Maven工程(注意,這裡可以不必創建Web工程),並添加SpringMVC的依賴,同時,這裡環境的搭建需要用到 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 配置

工程創建成功之後,首先添加 Spring 的配置文件,如下:

@Configuration
@ComponentScan(basePackages = "org.javaboy", useDefaultFilters = true,
excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes =
Controller.class)})
public class SpringConfig {
}

關於這個配置,我說如下幾點:

@Configuration 註解表示這是一個配置類,在我們這裡,這個配置的作用類似於applicationContext.xml

@ComponentScan 註解表示配置包掃描,裡邊的屬性和 xml 配置中的屬性都是一一對應的,useDefaultFilters 表示使用默認的過濾器,然後又除去 Controller 註解,即在 Spring 容器中掃描除了 Controller 之外的其他所有 Bean 。

3、 添加 SpringMVC 配置

接下來再來創建 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

此時,我們並沒 web.xml 文件,這時,我們可以使用Java代碼去代替 web.xml 文件,這裡會用到WebApplicationInitializer ,具體定義如下:

public class WebInit implements WebApplicationInitializer {
  public void onStartup(ServletContext servletContext) throws ServletException
{
    //首先來加載 SpringMVC 的配置文件
    AnnotationConfigWebApplicationContext ctx = new
AnnotationConfigWebApplicationContext();
    ctx.register(SpringMVCConfig.class);
    // 添加 DispatcherServlet
    ServletRegistration.Dynamic springmvc =
servletContext.addServlet("springmvc", new DispatcherServlet(ctx));
    // 給 DispatcherServlet 添加路徑映射
    springmvc.addMapping("/");
    // 給 DispatcherServlet 添加啟動時機
    springmvc.setLoadOnStartup(1);
 }
}

WebInit 的作用類似於 web.xml,這個類需要實現 WebApplicationInitializer 接口,並實現接口中的方法,當項目啟動時,onStartup 方法會被自動執行,我們可以在這個方法中做一些項目初始化操作,例如加載 SpringMVC 容器,添加過濾器,添加 Listener、添加 Servlet 等。

注意:

由於我們在WebInit中只是添加了SpringMVC的配置,這樣項目在啟動時只會去加載SpringMVC容器,而不會去加載 Spring 容器,如果一定要加載 Spring 容器,需要我們修改 SpringMVC 的配置,在SpringMVC 配置的包掃描中也去掃描 @Configuration 註解,進而加載 Spring 容器,還有一種方案可以解決這個問題,就是直接在項目中捨棄 Spring 配置,直接將所有配置放到 SpringMVC 的配置中來完成,這個在 SSM 整合時是沒有問題的,在實際開發中,較多採用第二種方案,第二種方案,SpringMVC 的配置如下:

@Configuration
@ComponentScan(basePackages = "org.javaboy")
public class SpringMVCConfig {
}

這種方案中,所有的註解都在 SpringMVC 中掃描,採用這種方案的話,則 Spring 的配置文件就可以刪除了。

5、測試

最後,添加一個 HelloController ,然後啟動項目進行測試:

@RestController
public class HelloController {
  @GetMapping("/hello")
  public String hello() {
    return "hello";
 }
}

啟動項目,訪問接口,結果如下:

在這裡插入圖片描述

Spring Boot全局異常處理

在Spring Boot項目中 ,異常統一處理,可以使用Spring中@ControllerAdvice來統一處理,也可以自己來定義異常處理方案。Spring Boot 中,對異常的處理有一些默認的策略,我們分別來看。

默認情況下,Spring Boot 中的異常頁面 是這樣的:

在這裡插入圖片描述

我們從這個異常提示中,也能看出來,之所以用戶看到這個頁面,是因為開發者沒有明確提供一個/error 路徑,如果開發者提供了 /error 路徑 ,這個頁面就不會展示出來,不過在 Spring Boot 中,提供/error 路徑實際上是下下策,Spring Boot本身在處理異常時,也是當所有條件都不滿足時,才會去找 /error 路徑。那麼我們就先來看看,在 Spring Boot 中,如何自定義 error 頁面,整體上來說,可以分為兩種,一種是靜態頁面,另一種是動態頁面。

靜態異常頁面

自定義靜態異常頁面,又分為兩種,第一種 是使用HTTP響應碼來命名頁面,例如404.html、405.html、500.html ….,另一種就是直接定義一個 4xx.html,表示400-499 的狀態都顯示這個異常頁面,5xx.html 表示 500-599 的狀態顯示這個異常頁面。

默認是在 classpath:/static/error/ 路徑下定義相關頁面:

在這裡插入圖片描述

此時,啟動項目,如果項目拋出 500 請求錯誤,就會自動展示 500.html 這個頁面,發生 404 就會展示404.html 頁面。如果異常展示頁面既存在 5xx.html,也存在 500.html ,此時,發生500異常時,優先展示 500.html 頁面。

動態異常頁面

動態的異常頁面定義方式和靜態的基本 一致,可以採用的頁面模板有 jsp、freemarker、thymeleaf。

動態異常頁面,也支持 404.html 或者 4xx.html ,但是一般來說,由於動態異常頁面可以直接展示異常詳細信息,所以就沒有必要挨個枚舉錯誤了 ,直接定義 4xx.html(這裡使用thymeleaf模板)或者5xx.html 即可。

注意,動態頁面模板,不需要開發者自己去定義控制器,直接定義異常頁面即可 ,Spring Boot 中自帶的異常處理器會自動查找到異常頁面。

頁面定義如下:
在這裡插入圖片描述

頁面內容如下:

<!DOCTYPE html>
<html lang="en" xmlns:th="//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>

默認情況下,完整的異常信息就是這5條,展示 效果如下 :
在這裡插入圖片描述

如果動態頁面和靜態頁面同時定義了異常處理頁面,例如 classpath:/static/error/404.html 和classpath:/templates/error/404.html 同時存在時,默認使用動態頁面。即完整的錯誤頁面查找

方式應該是這樣:

發生了 500 錯誤–>查找動態 500.html 頁面–>查找靜態 500.html –> 查找動態 5xx.html–>查找靜態5xx.html。

在這裡插入圖片描述

自定義異常數據

默認情況下,在 Spring Boot 中,所有的異常數據其實就是上文所展示出來的 5 條數據,這 5 條數據定義在 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 異常自動配置類中定義的,如果開發者沒有自己提供一個 ErrorAttributes 的實例的話,那麼 Spring Boot 將自動提供一個 ErrorAttributes 的實例,也就是 DefaultErrorAttributes 。

基於此 ,開發者自定義 ErrorAttributes 有兩種方式 :

  1. 直接實現 ErrorAttributes 接口

  2. 繼承 DefaultErrorAttributes(推薦),因為 DefaultErrorAttributes 中對異常數據的處理已經完成,開發者可以直接使用。

具體定義如下:

@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", "服務器內部錯誤!");
   }
    return map;
 }
}

定義好的 ErrorAttributes 一定要註冊成一個 Bean ,這樣,Spring Boot 就不會使用默認的DefaultErrorAttributes 了,運行效果如下圖:

在這裡插入圖片描述

自定義異常視圖

異常視圖默認就是前面所說的靜態或者動態頁面,這個也是可以自定義的,首先 ,默認的異常視圖加載邏輯在 org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController 類的errorHtml 方法中,這個方法用來返回異常頁面+數據,還有另外一個 error 方法,這個方法用來返回異常數據(如果是 ajax 請求,則該方法會被觸發)。

@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);
}

在該方法中 ,首先會通過 getErrorAttributes 方法去獲取異常數據(實際上會調用到 ErrorAttributes的實例 的 getErrorAttributes 方法),然後調用 resolveErrorView 去創建一個 ModelAndView ,如果這裡創建失敗,那麼用戶將會看到默認的錯誤提示頁面。

正常情況下, resolveErrorView 方法會來到 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;
}

在這裡,首先以異常響應碼作為視圖名分別去查找動態頁面和靜態頁面,如果沒有查找到,則再以 4xx或者 5xx 作為視圖名再去分別查找動態或者靜態頁面。

要自定義異常視圖解析,也很容易 ,由於 DefaultErrorViewResolver 是在ErrorMvcAutoConfiguration 類中提供的實例,即開發者沒有提供相關實例時,會使用默認的DefaultErrorViewResolver ,開發者提供了自己的 ErrorViewResolver 實例後,默認的配置就會失效,因此,自定義異常視圖,只需要提供 一個 ErrorViewResolver 的實例即可:

@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);
 }
}

實際上,開發者也可以在這裡定義異常數據(直接在 resolveErrorView 方法重新定義一個 model ,將參數中的model 數據拷貝過去並修改,注意參數中的 model 類型為 UnmodifiableMap,即不可以直接修改),而不需要自定義 MyErrorAttributes。定義完成後,提供一個名為 123 的視圖,如下圖:

在這裡插入圖片描述

如此之後,錯誤試圖就算定義成功了。

總結

實際上也可以自定義異常控制器 BasicErrorController ,不過我覺得這樣太大動干戈了,沒必要,前面幾種方式已經可以滿足我們的大部分開發需求了。如果是前後端分離架構,異常處理還有其他一些處理方案,這個以後和大家聊。

在這裡插入圖片描述

篇幅有限,其他內容就不在這裡一一展示了,這份Spring Boot實戰教程已整理成一份PDF文檔,共有200多頁。

關注公眾號:程序零世界,回復 666 獲取這份整理好的Spring Boot實戰教程。

在這裡插入圖片描述

最後

歡迎大家一起交流,喜歡文章記得點ge 贊喲,感謝支持!
file

Tags: