自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

使用枚舉簡(jiǎn)單封裝一個(gè)優(yōu)雅的 Spring Boot 全局異常處理!

開(kāi)發(fā) 前端
通過(guò)這篇文章,可以搞懂如何在 Spring Boot 中進(jìn)行異常處理。但是,光是會(huì)用了還不行,我們還要思考如何把異常處理這部分的代碼寫(xiě)的稍微優(yōu)雅一點(diǎn)。下面我會(huì)以我在工作中學(xué)到的一點(diǎn)實(shí)際項(xiàng)目中異常處理的方式,來(lái)說(shuō)說(shuō)我覺(jué)得稍微優(yōu)雅點(diǎn)的異常處理解決方案。

[[318861]]

 這篇文章鴿了很久,我在這篇文章 《用好 Java 中的枚舉,真的沒(méi)有那么簡(jiǎn)單!》 中就提到要分享。還是昨天一個(gè)讀者提醒我之后,我才發(fā)現(xiàn)自己沒(méi)有將這篇文章發(fā)到公眾號(hào)。說(shuō)到這里,我發(fā)現(xiàn)自己一個(gè)很大的問(wèn)題,就是有時(shí)候在文章里面說(shuō)要更新什么,結(jié)果后面就忘記了,很多時(shí)候不是自己沒(méi)寫(xiě),就因?yàn)楦鞣N事情混雜導(dǎo)致忘記發(fā)了。以后要盡量改正這個(gè)問(wèn)題!

在上一篇文章《SpringBoot 處理異常的幾種常見(jiàn)姿勢(shì)》中我介紹了:

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

通過(guò)這篇文章,可以搞懂如何在 Spring Boot 中進(jìn)行異常處理。但是,光是會(huì)用了還不行,我們還要思考如何把異常處理這部分的代碼寫(xiě)的稍微優(yōu)雅一點(diǎn)。下面我會(huì)以我在工作中學(xué)到的一點(diǎn)實(shí)際項(xiàng)目中異常處理的方式,來(lái)說(shuō)說(shuō)我覺(jué)得稍微優(yōu)雅點(diǎn)的異常處理解決方案。

下面僅僅是我作為一個(gè)我個(gè)人的角度來(lái)看的,如果各位讀者有更好的解決方案或者覺(jué)得本文提出的方案還有優(yōu)化的余地的話(huà),歡迎在評(píng)論區(qū)評(píng)論。

最終效果展示

下面先來(lái)展示一下完成后的效果,當(dāng)我們定義的異常被系統(tǒng)捕捉后返回給客戶(hù)端的信息是這樣的:

 

 

 

 

效果展示

返回的信息包含了異常下面 5 部分內(nèi)容:

  1. 唯一標(biāo)示異常的 code
  2. HTTP 狀態(tài)碼
  3. 錯(cuò)誤路徑
  4. 發(fā)生錯(cuò)誤的時(shí)間戳
  5. 錯(cuò)誤的具體信息

這樣返回異常信息,更利于我們前端根據(jù)異常信息做出相應(yīng)的表現(xiàn)。

異常處理核心代碼

ErrorCode.java (此枚舉類(lèi)中包含了異常的唯一標(biāo)識(shí)、HTTP 狀態(tài)碼以及錯(cuò)誤信息)

這個(gè)類(lèi)的主要作用就是統(tǒng)一管理系統(tǒng)中可能出現(xiàn)的異常,比較清晰明了。但是,可能出現(xiàn)的問(wèn)題是當(dāng)系統(tǒng)過(guò)于復(fù)雜,出現(xiàn)的異常過(guò)多之后,這個(gè)類(lèi)會(huì)比較龐大。有一種解決辦法:將多種相似的異常統(tǒng)一為一個(gè),比如將用戶(hù)找不到異常和訂單信息未找到的異常都統(tǒng)一為“未找到該資源”這一種異常,然后前端再對(duì)相應(yīng)的情況做詳細(xì)處理(我個(gè)人的一種處理方法,不敢保證是比較好的一種做法)。

 

  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, "請(qǐng)求數(shù)據(jù)格式驗(yàn)證失敗"); 
  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(返回給客戶(hù)端具體的異常對(duì)象)

這個(gè)類(lèi)作為異常信息返回給客戶(hù)端,里面包括了當(dāng)出現(xiàn)異常時(shí)我們想要返回給客戶(hù)端的所有信息。

 

  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 的抽象類(lèi),可以看做系統(tǒng)中其他異常類(lèi)的父類(lèi))

系統(tǒng)中的異常類(lèi)都要繼承自這個(gè)類(lèi)。

 

  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 (自定義異常)

可以看出通過(guò)繼承 BaseException 類(lèi)我們自定義異常會(huì)變的非常簡(jiǎn)單!

 

  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(全局異常捕獲)

我們定義了兩個(gè)異常捕獲方法。

這里再說(shuō)明一下,實(shí)際上這個(gè)類(lèi)只需要 handleAppException() 這一個(gè)方法就夠了,因?yàn)樗潜鞠到y(tǒng)所有異常的父類(lèi)。只要是拋出了繼承 BaseException 類(lèi)的異常后都會(huì)在這里被處理。

 

  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.     // 因?yàn)?nbsp;RuntimeException 是 BaseException 的父類(lèi) 
  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.     } 

(重要)一點(diǎn)擴(kuò)展:

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

答案:

會(huì)被handleResourceNotFoundException()方法捕獲。因?yàn)?@ExceptionHandler 捕獲異常的過(guò)程中,會(huì)優(yōu)先找到最匹配的。

下面通過(guò)源碼簡(jiǎn)單分析一下:

ExceptionHandlerMethodResolver.java中g(shù)etMappedMethod決定了具體被哪個(gè)方法處理。

 

  1. @Nullable 
  2.     private Method getMappedMethod(Class<? extends Throwable> exceptionType) { 
  3.         List<Class<? extends Throwable>> matches = new ArrayList<>(); 
  4.     //找到可以處理的所有異常信息。mappedMethods 中存放了異常和處理異常的方法的對(duì)應(yīng)關(guān)系 
  5.         for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) { 
  6.             if (mappedException.isAssignableFrom(exceptionType)) { 
  7.                 matches.add(mappedException); 
  8.             } 
  9.         } 
  10.     // 不為空說(shuō)明有方法處理異常 
  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()會(huì)首先找到可以匹配處理異常的所有方法信息,然后對(duì)其進(jìn)行從小到大的排序,最后取最小的那一個(gè)匹配的方法(即匹配度最高的那個(gè))。

寫(xiě)一個(gè)拋出異常的類(lèi)測(cè)試

Person.java

 

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

ExceptionController.java(拋出一場(chǎng)的類(lèi))

 

  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

 

責(zé)任編輯:武曉燕 來(lái)源: JavaGuide
相關(guān)推薦

2021-04-20 10:50:38

Spring Boot代碼Java

2022-10-26 07:14:25

Spring 6Spring業(yè)務(wù)

2024-10-24 08:21:33

2019-01-24 16:11:19

前端全局異常數(shù)據(jù)校驗(yàn)

2024-08-02 09:15:22

Spring捕捉格式

2023-09-24 13:55:42

Spring應(yīng)用程序

2025-02-07 09:11:04

JSON對(duì)象策略

2024-10-28 08:32:22

統(tǒng)一接口響應(yīng)SpringBoot響應(yīng)框架

2022-04-08 16:27:48

SpringBoot異常處理

2011-03-24 09:34:41

SPRING

2013-03-18 10:31:22

JS異常

2022-08-03 07:07:10

Spring數(shù)據(jù)封裝框架

2023-10-10 13:23:18

空指針異常Java

2024-09-27 12:27:31

2025-02-13 00:34:22

Spring對(duì)象系統(tǒng)

2024-11-11 11:30:34

2021-04-30 07:34:01

Spring BootController項(xiàng)目

2024-12-18 16:19:51

2020-11-13 07:08:51

Spring Boot應(yīng)用Spring

2025-04-22 08:20:51

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)