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

程式零世界發表於2020-07-15

引言

現在各大技術社群 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="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>

預設情況下,完整的異常資訊就是這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

相關文章