您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關Spring Boot如何統一處理全局異常的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
@ControllerAdvice注解是Spring3.2中新增的注解,學名是Controller增強器,作用是給Controller控制器添加統一的操作或處理。
這里ControllerAdvice也可以這么理解,其抽象級別應該是用于對Controller進行切面環繞的,而具體的業務織入方式則是通過結合其他的注解來實現的。@ControllerAdvice是在類上聲明的注解,其用法主要有三點:
1.結合方法型注解@ExceptionHandler,用于捕獲Controller中拋出的指定類型的異常,從而達到不同類型的異常區別處理的目的。
2.結合方法型注解@InitBinder,用于request中自定義參數解析方式進行注冊,從而達到自定義指定格式參數的目的。
3.結合方法型注解@ModelAttribute,表示其注解的方法將會在目標Controller方法執行之前執行。
從上面的講解可以看出,@ControllerAdvice的用法基本是將其聲明在某個bean上,然后在該bean的方法上使用其他的注解來指定不同的織入邏輯。不過這里@ControllerAdvice并不是使用AOP的方式來織入業務邏輯的,而是Spring內置對其各個邏輯的織入方式進行了內置支持。
針對聲明@ExceptionHandler 、 @InitBinder或@ModelAttribute方法的類的@Component @ExceptionHandler , @InitBinder在多個@Controller類之間共享。
使用@ControllerAdvice注解的類可以明確聲明為 Spring bean 或通過類路徑掃描自動檢測。 所有此類 bean 都根據Ordered語義或@Order / @Priority聲明進行Ordered , Ordered語義優先于@Order / @Priority聲明。 然后在運行時按該順序應用@ControllerAdvice bean。 但是請注意,實現PriorityOrdered @ControllerAdvice bean 的PriorityOrdered不高于實現Ordered @ControllerAdvice bean。 此外, Ordered不適用于范圍內的@ControllerAdvice例如,如果這樣的 bean 已被配置為請求范圍或會話范圍的 bean。 對于處理異常, @ExceptionHandler將在第一個具有匹配異常處理程序方法的通知中被選擇。 對于模型的屬性和數據綁定初始化, @ModelAttribute和@InitBinder方法將遵循@ControllerAdvice秩序。
注意:對于@ExceptionHandler方法,在特定建議 bean 的處理程序方法中,根異常匹配將優先于僅匹配當前異常的原因。 但是,與較低優先級建議 bean 上的任何匹配(無論是根還是原因級別)相比,更高優先級建議上的原因匹配仍然是首選。 因此,請在具有相應順序的優先建議 bean 上聲明您的主要根異常映射。
默認情況下, @ControllerAdvice ControllerAdvice 中的方法全局應用于所有控制器。 使用諸如annotations 、 basePackageClasses和basePackages (或其別名value )之類的選擇器來定義目標控制器的更窄子集。 如果聲明了多個選擇器,則應用布爾OR邏輯,這意味著所選控制器應至少匹配一個選擇器。 請注意,選擇器檢查是在運行時執行的,因此添加許多選擇器可能會對性能產生負面影響并增加復雜性。
配合 @ExceptionHandler注解結合使用,當異常拋到controller層時,可以對異常進行統一的處理,規定返回的json格式或者跳轉到指定的錯誤頁面等.
@ExceptionHandler的作用主要在于聲明一個或多個類型的異常,當符合條件的Controller拋出這些異常之后將會對這些異常進行捕獲,然后按照其標注的方法的邏輯進行處理,從而改變返回的視圖信息。
用于處理特定處理程序類和/或處理程序方法中的異常的注解。
使用此注解注釋的處理程序方法允許具有非常靈活的簽名。 它們可能具有以下類型的參數,按任意順序排列:
異常參數:聲明為一般異常或更具體的異常。 如果注解本身沒有通過其value()縮小異常類型,這也可用作映射提示
/** * 自定義一個異常類,用于處理我們發生的業務異常 * * @author Promsing(張有博) * @version 1.0.0 * @since 2021/11/27 - 20:14 */ public class BizException extends RuntimeException { private static final long serialVersionUID = 1L; /** * 錯誤碼 */ protected String errorCode; /** * 錯誤信息 */ protected String errorMsg; public BizException() { super(); } public BizException(FrontResult errorInfoInterface) { super(errorInfoInterface.getCode()); this.errorCode = errorInfoInterface.getMessage(); this.errorMsg = errorInfoInterface.getMessage(); } public BizException(FrontResult errorInfoInterface, Throwable cause) { super(errorInfoInterface.getCode(), cause); this.errorCode = errorInfoInterface.getCode(); this.errorMsg = errorInfoInterface.getMessage(); } public BizException(String errorMsg) { super(errorMsg); this.errorMsg = errorMsg; } public BizException(String errorCode, String errorMsg) { super(errorCode); this.errorCode = errorCode; this.errorMsg = errorMsg; } public BizException(String errorCode, String errorMsg, Throwable cause) { super(errorCode, cause); this.errorCode = errorCode; this.errorMsg = errorMsg; } public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } public String getMessage() { return errorMsg; } @Override public Throwable fillInStackTrace() { return this; } }
import com.tfjy.arbackend.enumtool.ResultCodeEnum; import com.tfjy.arbackend.enumtool.ResutlMsgEnum; import com.tfjy.arbackend.util.FrontResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.sql.SQLException; /** * 統一異常處理 * * @author Promsing(張有博) * @version 1.0.0 * @since 2021/11/27 - 20:14 */ @ControllerAdvice//使用該注解表示開啟了全局異常的捕獲 public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * 處理自定義的業務異常 * @param req * @param e * @return */ @ExceptionHandler(value = BizException.class) @ResponseBody public FrontResult bizExceptionHandler(HttpServletRequest req, BizException e){ logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("發生業務異常!原因是:{}",e.getErrorMsg()); return FrontResult.getExceptionResult(e.getErrorCode(),e.getErrorMsg()); } /** * 處理空指針的異常 * @param req * @param e * @return */ @ExceptionHandler(value =NullPointerException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, NullPointerException e) { logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("發生空指針異常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 處理索引越界異常 * @param req * @param e * @return */ @ExceptionHandler(value =IndexOutOfBoundsException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, IndexOutOfBoundsException e){ logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("索引越界異常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 處理類未找到異常 * @param req * @param e * @return */ @ExceptionHandler(value =ClassNotFoundException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, ClassNotFoundException e) { logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("發生類未找到異常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 處理SQL異常 * @param req * @param e * @return */ @ExceptionHandler(value = SQLException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, SQLException e) { logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("發生SQL異常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 處理IO異常 * @param req * @param e * @return */ @ExceptionHandler(value = IOException.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, IOException e) { logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("發生IO異常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } /** * 處理其他異常 * @param req * @param e * @return */ @ExceptionHandler(value =Exception.class) @ResponseBody public FrontResult exceptionHandler(HttpServletRequest req, Exception e){ logger.error("URL : " + req.getRequestURL().toString()); logger.error("HTTP_METHOD : " + req.getMethod()); logger.error("未知異常!原因是:",e); return FrontResult.getExceptionResult(ResultCodeEnum.FAIL.getCode(), ResutlMsgEnum.EXECUTE_FAIL.getMsg()); } }
import com.tfjy.arbackend.enumtool.ResultCodeEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class FrontResult { /** * 結果狀態碼 */ private String code; /** * 響應結果描述 */ private String message; /** * 返回數據 */ private Object data; /** * 靜態方法,返回前端實體結果 * * @param code 狀態碼 * @param message 消息 * @param data 數據 * @return 前端實體結果 */ public static FrontResult build(String code, String message, Object data) { return new FrontResult(code, message, data); } /** * 返回成功的結果實體 * * @param message 消息 * @param data 數據 * @return 實體 */ public static FrontResult getSuccessResult(String message, Object data) { FrontResult result = new FrontResult(); result.code = ResultCodeEnum.SUCCESS.getCode(); result.message = message; result.data = data; return result; } /** * 返回無需data的成功結果實體 * * @param message 消息內容 * @return 返回結果 */ public static FrontResult getSuccessResultOnlyMessage(String message) { FrontResult result = new FrontResult(); result.code = ResultCodeEnum.SUCCESS.getCode(); result.message = message; result.data = null; return result; } /** * 獲取一個異常結果 * * @param code 錯誤碼 * @param message 自定義異常信息 * @return FrontResult */ public static FrontResult getExceptionResult(String code, String message) { FrontResult result = new FrontResult(); result.code = code.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getCode() : code; result.message = message.isEmpty() ? ResultCodeEnum.CODE_EXCEPTION.getMsg() : message; return result; } }
import lombok.AllArgsConstructor; @AllArgsConstructor public enum ResultCodeEnum { // 請求成功 SUCCESS("0000"), // 請求失敗 FAIL("1111"), // EXCEL 導入失敗 EXCEL_FAIL("1000"), // userID 為空 ID_NULL("1001"), // 前端傳的實體為空 MODEL_NULL("1002"), // 更新失敗 UPDATE_FAIL("1011"), // 參數為空 PARAM_ERROR("400"), // 代碼內部異常 CODE_EXCEPTION("500", "代碼內部異常"); /** * 狀態碼 */ private String code; public String getCode() { return code; } ResultCodeEnum(String code) { this.code = code; } private String msg; public String getMsg() { return msg; } } public enum ResutlMsgEnum { //查詢成功 FIND_SUCCESS("查詢成功!"), //查詢失敗 FIND_FAIL("查詢失敗!"), //更新成功 UPDATE_SUCCESS("更新成功"), //更新失敗 UPDATE_FAIL("更新成功"), SEND_SUCCESS("發送成功"), SEND_FAIL("發送失敗"); private String msg; ResutlMsgEnum(String msg) { this.msg = msg; } public String getMsg() { return msg; } }
/** * 測試用例 * * @author Promsing(張有博) * @version 1.0.0 * @since 2021/11/29 - 9:05 */ @Api(tags = {"測試controller"}) @RequestMapping(value = "/testController") @RestController public class TestController { @ApiOperation(value = "測試null") @GetMapping(value = "getNull") public FrontResult getNull() { int length = 0; String name=null; length = name.length(); return FrontResult.build(ResultCodeEnum.SUCCESS.getCode(), ResutlMsgEnum.EXECUTE_SUCCESS.getMsg(), length); } }
默認情況下,Spring Boot為兩種情況提供了不同的響應方式。
一種是瀏覽器客戶端請求一個不存在的頁面或服務端處理發生異常時,一般情況下瀏覽器默認發送的請求頭中Accept: text/html,所以Spring Boot默認會響應一個html文檔內容,稱作“Whitelabel Error Page”。
另一種是使用Postman等調試工具發送請求一個不存在的url或服務端處理發生異常時,Spring Boot會返回類似如下的Json格式字符串信息
{ "timestamp": "2018-05-12T06:11:45.209+0000", "status": 404, "error": "Not Found", "message": "No message available", "path": "/index.html" }
原理也很簡單,Spring Boot 默認提供了程序出錯的結果映射路徑/error。這個/error請求會在BasicErrorController中處理,其內部是通過判斷請求頭中的Accept的內容是否為text/html來區分請求是來自客戶端瀏覽器(瀏覽器通常默認自動發送請求頭內容Accept:text/html)還是客戶端接口的調用,以此來決定返回頁面視圖還是 JSON 消息內容。
相關BasicErrorController中代碼如下:
感謝各位的閱讀!關于“Spring Boot如何統一處理全局異常”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。