您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關如何在Spring Boot中進行異常處理,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
通過這篇文章,可以搞懂如何在 Spring Boot 中進行異常處理。但是,光是會用了還不行,我們還要思考如何把異常處理這部分的代碼寫的稍微優雅一點。下面我會以我在工作中學到的一點實際項目中異常處理的方式,來說說我覺得稍微優雅點的異常處理解決方案。
下面僅僅是我作為一個我個人的角度來看的,如果各位讀者有更好的解決方案或者覺得本文提出的方案還有優化的余地的話,歡迎在評論區評論。
最終效果展示
下面先來展示一下完成后的效果,當我們定義的異常被系統捕捉后返回給客戶端的信息是這樣的:
效果展示
返回的信息包含了異常下面 5 部分內容:
鴻蒙官方戰略合作共建——HarmonyOS技術社區
唯一標示異常的 code
HTTP 狀態碼
錯誤路徑
發生錯誤的時間戳
錯誤的具體信息
這樣返回異常信息,更利于我們前端根據異常信息做出相應的表現。
異常處理核心代碼
ErrorCode.java (此枚舉類中包含了異常的唯一標識、HTTP 狀態碼以及錯誤信息)
這個類的主要作用就是統一管理系統中可能出現的異常,比較清晰明了。但是,可能出現的問題是當系統過于復雜,出現的異常過多之后,這個類會比較龐大。有一種解決辦法:將多種相似的異常統一為一個,比如將用戶找不到異常和訂單信息未找到的異常都統一為“未找到該資源”這一種異常,然后前端再對相應的情況做詳細處理(我個人的一種處理方法,不敢保證是比較好的一種做法)。
import org.springframework.http.HttpStatus; public enum ErrorCode { RESOURCE_NOT_FOUND(1001, HttpStatus.NOT_FOUND, "未找到該資源"), REQUEST_VALIDATION_FAILED(1002, HttpStatus.BAD_REQUEST, "請求數據格式驗證失敗"); private final int code; private final HttpStatus status; private final String message; ErrorCode(int code, HttpStatus status, String message) { this.code = code; this.status = status; this.message = message; } public int getCode() { return code; } public HttpStatus getStatus() { return status; } public String getMessage() { return message; } @Override public String toString() { return "ErrorCode{" + "code=" + code + ", status=" + status + ", message='" + message + '\'' + '}'; } }
ErrorReponse.java(返回給客戶端具體的異常對象)
這個類作為異常信息返回給客戶端,里面包括了當出現異常時我們想要返回給客戶端的所有信息。
import org.springframework.util.ObjectUtils; import java.time.Instant; import java.util.HashMap; import java.util.Map; public class ErrorReponse { private int code; private int status; private String message; private String path; private Instant timestamp; private HashMap<String, Object> data = new HashMap<String, Object>(); public ErrorReponse() { } public ErrorReponse(BaseException ex, String path) { this(ex.getError().getCode(), ex.getError().getStatus().value(), ex.getError().getMessage(), path, ex.getData()); } public ErrorReponse(int code, int status, String message, String path, Map<String, Object> data) { this.code = code; this.status = status; this.message = message; this.path = path; this.timestamp = Instant.now(); if (!ObjectUtils.isEmpty(data)) { this.data.putAll(data); } } // 省略 getter/setter 方法 @Override public String toString() { return "ErrorReponse{" + "code=" + code + ", status=" + status + ", message='" + message + '\'' + ", path='" + path + '\'' + ", timestamp=" + timestamp + ", data=" + data + '}'; } }
BaseException.java(繼承自 RuntimeException 的抽象類,可以看做系統中其他異常類的父類)
系統中的異常類都要繼承自這個類。
public abstract class BaseException extends RuntimeException { private final ErrorCode error; private final HashMap<String, Object> data = new HashMap<>(); public BaseException(ErrorCode error, Map<String, Object> data) { super(error.getMessage()); this.error = error; if (!ObjectUtils.isEmpty(data)) { this.data.putAll(data); } } protected BaseException(ErrorCode error, Map<String, Object> data, Throwable cause) { super(error.getMessage(), cause); this.error = error; if (!ObjectUtils.isEmpty(data)) { this.data.putAll(data); } } public ErrorCode getError() { return error; } public Map<String, Object> getData() { return data; } }
ResourceNotFoundException.java (自定義異常)
可以看出通過繼承 BaseException 類我們自定義異常會變的非常簡單!
import java.util.Map; public class ResourceNotFoundException extends BaseException { public ResourceNotFoundException(Map<String, Object> data) { super(ErrorCode.RESOURCE_NOT_FOUND, data); } }
GlobalExceptionHandler.java(全局異常捕獲)
我們定義了兩個異常捕獲方法。
這里再說明一下,實際上這個類只需要 handleAppException() 這一個方法就夠了,因為它是本系統所有異常的父類。只要是拋出了繼承 BaseException 類的異常后都會在這里被處理。
import com.twuc.webApp.web.ExceptionController; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; @ControllerAdvice(assignableTypes = {ExceptionController.class}) @ResponseBody public class GlobalExceptionHandler { // 也可以將 BaseException 換為 RuntimeException // 因為 RuntimeException 是 BaseException 的父類 @ExceptionHandler(BaseException.class) public ResponseEntity<?> handleAppException(BaseException ex, HttpServletRequest request) { ErrorReponse representation = new ErrorReponse(ex, request.getRequestURI()); return new ResponseEntity<>(representation, new HttpHeaders(), ex.getError().getStatus()); } @ExceptionHandler(value = ResourceNotFoundException.class) public ResponseEntity<ErrorReponse> handleResourceNotFoundException(ResourceNotFoundException ex, HttpServletRequest request) { ErrorReponse errorReponse = new ErrorReponse(ex, request.getRequestURI()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorReponse); } }
(重要)一點擴展:
哈哈!實際上我多加了一個算是多余的異常捕獲方法handleResourceNotFoundException() 主要是為了考考大家當我們拋出了 ResourceNotFoundException異常會被下面哪一個方法捕獲呢?
答案:
會被handleResourceNotFoundException()方法捕獲。因為 @ExceptionHandler 捕獲異常的過程中,會優先找到最匹配的。
下面通過源碼簡單分析一下:
ExceptionHandlerMethodResolver.java中getMappedMethod決定了具體被哪個方法處理。
@Nullable private Method getMappedMethod(Class<? extends Throwable> exceptionType) { List<Class<? extends Throwable>> matches = new ArrayList<>(); //找到可以處理的所有異常信息。mappedMethods 中存放了異常和處理異常的方法的對應關系 for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) { if (mappedException.isAssignableFrom(exceptionType)) { matches.add(mappedException); } } // 不為空說明有方法處理異常 if (!matches.isEmpty()) { // 按照匹配程度從小到大排序 matches.sort(new ExceptionDepthComparator(exceptionType)); // 返回處理異常的方法 return this.mappedMethods.get(matches.get(0)); } else { return null; } }
從源代碼看出:getMappedMethod()會首先找到可以匹配處理異常的所有方法信息,然后對其進行從小到大的排序,最后取最小的那一個匹配的方法(即匹配度最高的那個)。
寫一個拋出異常的類測試
Person.java
public class Person { private Long id; private String name; // 省略 getter/setter 方法 }
ExceptionController.java(拋出一場的類)
@RestController @RequestMapping("/api") public class ExceptionController { @GetMapping("/resourceNotFound") public void throwException() { Person p=new Person(1L,"SnailClimb"); throw new ResourceNotFoundException(ImmutableMap.of("person id:", p.getId())); } }
關于如何在Spring Boot中進行異常處理就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。