成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

使用枚舉簡單封裝一個優雅的 Spring Boot 全局異常處理!

開發 前端
通過這篇文章,可以搞懂如何在 Spring Boot 中進行異常處理。但是,光是會用了還不行,我們還要思考如何把異常處理這部分的代碼寫的稍微優雅一點。下面我會以我在工作中學到的一點實際項目中異常處理的方式,來說說我覺得稍微優雅點的異常處理解決方案。

[[318861]]

 這篇文章鴿了很久,我在這篇文章 《用好 Java 中的枚舉,真的沒有那么簡單!》 中就提到要分享。還是昨天一個讀者提醒我之后,我才發現自己沒有將這篇文章發到公眾號。說到這里,我發現自己一個很大的問題,就是有時候在文章里面說要更新什么,結果后面就忘記了,很多時候不是自己沒寫,就因為各種事情混雜導致忘記發了。以后要盡量改正這個問題!

在上一篇文章《SpringBoot 處理異常的幾種常見姿勢》中我介紹了:

  1. 使用 @ControllerAdvice 和 @ExceptionHandler 處理全局異常
  2. @ExceptionHandler 處理 Controller 級別的異常
  3. ResponseStatusException

通過這篇文章,可以搞懂如何在 Spring Boot 中進行異常處理。但是,光是會用了還不行,我們還要思考如何把異常處理這部分的代碼寫的稍微優雅一點。下面我會以我在工作中學到的一點實際項目中異常處理的方式,來說說我覺得稍微優雅點的異常處理解決方案。

下面僅僅是我作為一個我個人的角度來看的,如果各位讀者有更好的解決方案或者覺得本文提出的方案還有優化的余地的話,歡迎在評論區評論。

最終效果展示

下面先來展示一下完成后的效果,當我們定義的異常被系統捕捉后返回給客戶端的信息是這樣的:

 

 

 

 

效果展示

返回的信息包含了異常下面 5 部分內容:

  1. 唯一標示異常的 code
  2. HTTP 狀態碼
  3. 錯誤路徑
  4. 發生錯誤的時間戳
  5. 錯誤的具體信息

這樣返回異常信息,更利于我們前端根據異常信息做出相應的表現。

異常處理核心代碼

ErrorCode.java (此枚舉類中包含了異常的唯一標識、HTTP 狀態碼以及錯誤信息)

這個類的主要作用就是統一管理系統中可能出現的異常,比較清晰明了。但是,可能出現的問題是當系統過于復雜,出現的異常過多之后,這個類會比較龐大。有一種解決辦法:將多種相似的異常統一為一個,比如將用戶找不到異常和訂單信息未找到的異常都統一為“未找到該資源”這一種異常,然后前端再對相應的情況做詳細處理(我個人的一種處理方法,不敢保證是比較好的一種做法)。

 

  1. import org.springframework.http.HttpStatus; 
  2.  
  3.  
  4. public enum ErrorCode { 
  5.  
  6.     RESOURCE_NOT_FOUND(1001, HttpStatus.NOT_FOUND, "未找到該資源"), 
  7.     REQUEST_VALIDATION_FAILED(1002, HttpStatus.BAD_REQUEST, "請求數據格式驗證失敗"); 
  8.     private final int code; 
  9.  
  10.     private final HttpStatus status; 
  11.  
  12.     private final String message; 
  13.  
  14.     ErrorCode(int code, HttpStatus status, String message) { 
  15.         this.code = code; 
  16.         this.status = status; 
  17.         this.message = message; 
  18.     } 
  19.  
  20.     public int getCode() { 
  21.         return code; 
  22.     } 
  23.  
  24.     public HttpStatus getStatus() { 
  25.         return status; 
  26.     } 
  27.  
  28.     public String getMessage() { 
  29.         return message; 
  30.     } 
  31.  
  32.     @Override 
  33.     public String toString() { 
  34.         return "ErrorCode{" + 
  35.                 "code=" + code + 
  36.                 ", status=" + status + 
  37.                 ", message='" + message + '\'' + 
  38.                 '}'
  39.     } 

ErrorReponse.java(返回給客戶端具體的異常對象)

這個類作為異常信息返回給客戶端,里面包括了當出現異常時我們想要返回給客戶端的所有信息。

 

  1. import org.springframework.util.ObjectUtils; 
  2.  
  3. import java.time.Instant; 
  4. import java.util.HashMap; 
  5. import java.util.Map; 
  6.  
  7. public class ErrorReponse { 
  8.     private int code; 
  9.     private int status; 
  10.     private String message; 
  11.     private String path; 
  12.     private Instant timestamp
  13.     private HashMap<String, Object> data = new HashMap<String, Object>(); 
  14.  
  15.     public ErrorReponse() { 
  16.     } 
  17.  
  18.     public ErrorReponse(BaseException ex, String path) { 
  19.         this(ex.getError().getCode(), ex.getError().getStatus().value(), ex.getError().getMessage(), path, ex.getData()); 
  20.     } 
  21.  
  22.     public ErrorReponse(int code, int status, String message, String path, Map<String, Object> data) { 
  23.         this.code = code; 
  24.         this.status = status; 
  25.         this.message = message; 
  26.         this.path = path; 
  27.         this.timestamp = Instant.now(); 
  28.         if (!ObjectUtils.isEmpty(data)) { 
  29.             this.data.putAll(data); 
  30.         } 
  31.     } 
  32.  
  33. // 省略 getter/setter 方法 
  34.  
  35.     @Override 
  36.     public String toString() { 
  37.         return "ErrorReponse{" + 
  38.                 "code=" + code + 
  39.                 ", status=" + status + 
  40.                 ", message='" + message + '\'' + 
  41.                 ", path='" + path + '\'' + 
  42.                 ", timestamp=" + timestamp + 
  43.                 ", data=" + data + 
  44.                 '}'
  45.     } 

BaseException.java(繼承自 RuntimeException 的抽象類,可以看做系統中其他異常類的父類)

系統中的異常類都要繼承自這個類。

 

  1. public abstract class BaseException extends RuntimeException { 
  2.     private final ErrorCode error; 
  3.     private final HashMap<String, Object> data = new HashMap<>(); 
  4.  
  5.     public BaseException(ErrorCode error, Map<String, Object> data) { 
  6.         super(error.getMessage()); 
  7.         this.error = error; 
  8.         if (!ObjectUtils.isEmpty(data)) { 
  9.             this.data.putAll(data); 
  10.         } 
  11.     } 
  12.  
  13.     protected BaseException(ErrorCode error, Map<String, Object> data, Throwable cause) { 
  14.         super(error.getMessage(), cause); 
  15.         this.error = error; 
  16.         if (!ObjectUtils.isEmpty(data)) { 
  17.             this.data.putAll(data); 
  18.         } 
  19.     } 
  20.  
  21.     public ErrorCode getError() { 
  22.         return error; 
  23.     } 
  24.  
  25.     public Map<String, Object> getData() { 
  26.         return data; 
  27.     } 
  28.  

ResourceNotFoundException.java (自定義異常)

可以看出通過繼承 BaseException 類我們自定義異常會變的非常簡單!

 

  1. import java.util.Map; 
  2.  
  3. public class ResourceNotFoundException extends BaseException { 
  4.  
  5.     public ResourceNotFoundException(Map<String, Object> data) { 
  6.         super(ErrorCode.RESOURCE_NOT_FOUND, data); 
  7.     } 

GlobalExceptionHandler.java(全局異常捕獲)

我們定義了兩個異常捕獲方法。

這里再說明一下,實際上這個類只需要 handleAppException() 這一個方法就夠了,因為它是本系統所有異常的父類。只要是拋出了繼承 BaseException 類的異常后都會在這里被處理。

 

  1. import com.twuc.webApp.web.ExceptionController; 
  2. import org.springframework.http.HttpHeaders; 
  3. import org.springframework.http.HttpStatus; 
  4. import org.springframework.http.ResponseEntity; 
  5. import org.springframework.web.bind.annotation.ControllerAdvice; 
  6. import org.springframework.web.bind.annotation.ExceptionHandler; 
  7. import org.springframework.web.bind.annotation.ResponseBody; 
  8. import javax.servlet.http.HttpServletRequest; 
  9.  
  10. @ControllerAdvice(assignableTypes = {ExceptionController.class}) 
  11. @ResponseBody 
  12. public class GlobalExceptionHandler { 
  13.  
  14.     // 也可以將 BaseException 換為 RuntimeException 
  15.     // 因為 RuntimeException 是 BaseException 的父類 
  16.     @ExceptionHandler(BaseException.class) 
  17.     public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) { 
  18.         ErrorReponse representation = new ErrorReponse(ex, request.getRequestURI()); 
  19.         return new ResponseEntity<>(representation, new HttpHeaders(), ex.getError().getStatus()); 
  20.     } 
  21.  
  22.     @ExceptionHandler(value = ResourceNotFoundException.class) 
  23.     public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) { 
  24.         ErrorReponse errorReponse = new ErrorReponse(ex, request.getRequestURI()); 
  25.         return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorReponse); 
  26.     } 

(重要)一點擴展:

哈哈!實際上我多加了一個算是多余的異常捕獲方法handleResourceNotFoundException() 主要是為了考考大家當我們拋出了 ResourceNotFoundException異常會被下面哪一個方法捕獲呢?

答案:

會被handleResourceNotFoundException()方法捕獲。因為 @ExceptionHandler 捕獲異常的過程中,會優先找到最匹配的。

下面通過源碼簡單分析一下:

ExceptionHandlerMethodResolver.java中getMappedMethod決定了具體被哪個方法處理。

 

  1. @Nullable 
  2.     private Method getMappedMethod(Class<? extends Throwable> exceptionType) { 
  3.         List<Class<? extends Throwable>> matches = new ArrayList<>(); 
  4.     //找到可以處理的所有異常信息。mappedMethods 中存放了異常和處理異常的方法的對應關系 
  5.         for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) { 
  6.             if (mappedException.isAssignableFrom(exceptionType)) { 
  7.                 matches.add(mappedException); 
  8.             } 
  9.         } 
  10.     // 不為空說明有方法處理異常 
  11.         if (!matches.isEmpty()) { 
  12.       // 按照匹配程度從小到大排序 
  13.             matches.sort(new ExceptionDepthComparator(exceptionType)); 
  14.       // 返回處理異常的方法 
  15.             return this.mappedMethods.get(matches.get(0)); 
  16.         } 
  17.         else { 
  18.             return null
  19.         } 
  20.     } 

從源代碼看出:getMappedMethod()會首先找到可以匹配處理異常的所有方法信息,然后對其進行從小到大的排序,最后取最小的那一個匹配的方法(即匹配度最高的那個)。

寫一個拋出異常的類測試

Person.java

 

  1. public class Person { 
  2.     private Long id; 
  3.     private String name
  4.  
  5.     // 省略 getter/setter 方法 

ExceptionController.java(拋出一場的類)

 

  1. @RestController 
  2. @RequestMapping("/api"
  3. public class ExceptionController { 
  4.  
  5.     @GetMapping("/resourceNotFound"
  6.     public void throwException() { 
  7.         Person p=new Person(1L,"SnailClimb"); 
  8.         throw new ResourceNotFoundException(ImmutableMap.of("person id:", p.getId())); 
  9.     } 
  10.  

源碼地址:https://github.com/Snailclimb/springboot-guide/tree/master/source-code/basis/springboot-handle-exception-improved

 

責任編輯:武曉燕 來源: JavaGuide
相關推薦

2021-04-20 10:50:38

Spring Boot代碼Java

2022-10-26 07:14:25

Spring 6Spring業務

2024-10-24 08:21:33

2019-01-24 16:11:19

前端全局異常數據校驗

2024-08-02 09:15:22

Spring捕捉格式

2023-09-24 13:55:42

Spring應用程序

2025-02-07 09:11:04

JSON對象策略

2024-10-28 08:32:22

統一接口響應SpringBoot響應框架

2011-03-24 09:34:41

SPRING

2022-04-08 16:27:48

SpringBoot異常處理

2013-03-18 10:31:22

JS異常

2022-08-03 07:07:10

Spring數據封裝框架

2023-10-10 13:23:18

空指針異常Java

2024-09-27 12:27:31

2025-02-13 00:34:22

Spring對象系統

2024-11-11 11:30:34

2021-04-30 07:34:01

Spring BootController項目

2024-12-18 16:19:51

2025-05-23 10:20:00

2025-04-22 08:20:51

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲国产精品久久久久婷婷老年 | 国产精品久久久久久久久久久免费看 | 亚洲精品不卡 | 成人精品一区二区三区中文字幕 | 天天操天天插天天干 | 精品国产伦一区二区三区观看体验 | 欧美日韩欧美 | 国产精品一区二 | 国产一级特黄aaa大片评分 | 久久久性色精品国产免费观看 | 中文字幕日韩欧美一区二区三区 | 91成人免费观看 | 欧美日韩在线观看一区 | av网站在线看 | 91亚洲欧美 | 成年人国产在线观看 | 成人精品一区二区户外勾搭野战 | 国产精品一区二区福利视频 | 国内精品久久久久 | 欧美日韩久 | 蜜臀久久99精品久久久久野外 | 天天插天天操 | 一区在线播放 | 国产情品 | 久久最新精品 | 中文字幕爱爱视频 | 国产精品久久久久久久免费大片 | 91视频正在播放 | av在线天堂网 | 亚洲一区二区三区免费观看 | 精品欧美一区二区三区久久久 | 国产综合精品一区二区三区 | 黄色网页在线 | 日韩免费网站 | 亚洲国产成人在线视频 | 亚洲一区在线日韩在线深爱 | 91电影| 九九九久久国产免费 | 免费观看av | 亚洲免费视频在线观看 | 中文字幕在线电影观看 |