1.@ControllerAdvice
1.場景一
在構建RestFul的今天,我們一般會限定好返回數據的格式比如:
{ "code": 0, "data": {}, "msg": "操作成功" }
但有時卻往往會產生一些bug。這時候就破壞了返回數據的一致性,導致調用者無法解析。所以我們常常會定義一個全局的異常攔截器。
2.場景二
對於與數據庫相關的 Spring MVC 項目,我們通常會把 事務 配置在 Service層,當數據庫操作失敗時讓 Service 層拋出運行時異常,Spring 事物管理器就會進行回滾。
如此一來,我們的 Controller 層就不得不進行 try-catch Service 層的異常,否則會返回一些不友好的錯誤信息到客戶端。但是,Controller 層每個方法體都寫一些模板化的 try-catch 的代碼,很難看也難維護,特別是還需要對 Service 層的不同異常進行不同處理的時候。
@ControllerAdvice + @ExceptionHandler 進行全局的 Controller 層異常處理,只要設計得當,就再也不用在 Controller 層進行 try-catch 了!而且,@Validated 校驗器注解的異常,也可以一起處理,無需手動判斷綁定校驗結果 BindingResult/Errors 了
3.@ControllerAdvice的使用
1.注意
- 優點:將 Controller 層的異常和數據校驗的異常進行統一處理,減少模板代碼,減少編碼量,提升擴展性和可維護性。
- 缺點:只能處理 Controller 層未捕獲(往外拋)的異常,對於 Interceptor(攔截器)層的異常,Spring 框架層的異常,就無能為力了。
2.@ControllerAdvice
該注解是springMVC的注解
-
@ControllerAdvice
是一個@Component
,用於定義@ExceptionHandler
,@InitBinder
和@ModelAttribute
方法,適用於所有使用@RequestMapping
方法。 -
Spring4之前,
@ControllerAdvice
在同一調度的Servlet中協助所有控制器。Spring4已經改變:@ControllerAdvice
支持配置控制器的子集,而默認的行為仍然可以利用。 -
在Spring4中,
@ControllerAdvice
通過annotations()
,basePackageClasses()
,basePackages()
方法定制用於選擇控制器子集。
不過據經驗之談,只有配合@ExceptionHandler
最有用,其它兩個不常用。
3.使用
@ControllerAdvice 注解定義全局異常處理類
@ControllerAdvice public class GlobalExceptionHandler { }
@ExceptionHandler 注解聲明異常處理方法
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseBody String handleException(){ return "Exception Deal!"; } }
方法 handleException() 就會處理所有 Controller 層拋出的 Exception 及其子類的異常,這是最基本的用法了。
被 @ExceptionHandler 注解的方法的參數列表里,還可以聲明很多種類型的參數,詳見文檔。其原型如下:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExceptionHandler { /** * Exceptions handled by the annotated method. If empty, will default to any * exceptions listed in the method argument list. */ Class<? extends Throwable>[] value() default {}; }
如果 @ExceptionHandler 注解中未聲明要處理的異常類型,則默認為參數列表中的異常類型。所以上面的寫法,還可以寫成這樣:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler() @ResponseBody String handleException(Exception e){ return "Exception Deal! " + e.getMessage(); } }
參數對象就是 Controller 層拋出的異常對象!
示例
package com.cmc.schedule.handler; import com.gionee.base.entity.JsonResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.ConversionNotSupportedException; import org.springframework.beans.TypeMismatchException; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import java.io.IOException; /** * 異常攔截處理器 * * @author chenmc */ @ControllerAdvice @ResponseBody public class GlobalExceptionHandler { private static final String logExceptionFormat = "Capture Exception By GlobalExceptionHandler: Code: %s Detail: %s"; private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); //運行時異常 @ExceptionHandler(RuntimeException.class) public String runtimeExceptionHandler(RuntimeException ex) { return resultFormat(1, ex); } //空指針異常 @ExceptionHandler(NullPointerException.class) public String nullPointerExceptionHandler(NullPointerException ex) { return resultFormat(2, ex); } //類型轉換異常 @ExceptionHandler(ClassCastException.class) public String classCastExceptionHandler(ClassCastException ex) { return resultFormat(3, ex); } //IO異常 @ExceptionHandler(IOException.class) public String iOExceptionHandler(IOException ex) { return resultFormat(4, ex); } //未知方法異常 @ExceptionHandler(NoSuchMethodException.class) public String noSuchMethodExceptionHandler(NoSuchMethodException ex) { return resultFormat(5, ex); } //數組越界異常 @ExceptionHandler(IndexOutOfBoundsException.class) public String indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) { return resultFormat(6, ex); } //400錯誤 @ExceptionHandler({HttpMessageNotReadableException.class}) public String requestNotReadable(HttpMessageNotReadableException ex) { System.out.println("400..requestNotReadable"); return resultFormat(7, ex); } //400錯誤 @ExceptionHandler({TypeMismatchException.class}) public String requestTypeMismatch(TypeMismatchException ex) { System.out.println("400..TypeMismatchException"); return resultFormat(8, ex); } //400錯誤 @ExceptionHandler({MissingServletRequestParameterException.class}) public String requestMissingServletRequest(MissingServletRequestParameterException ex) { System.out.println("400..MissingServletRequest"); return resultFormat(9, ex); } //405錯誤 @ExceptionHandler({HttpRequestMethodNotSupportedException.class}) public String request405(HttpRequestMethodNotSupportedException ex) { return resultFormat(10, ex); } //406錯誤 @ExceptionHandler({HttpMediaTypeNotAcceptableException.class}) public String request406(HttpMediaTypeNotAcceptableException ex) { System.out.println("406..."); return resultFormat(11, ex); } //500錯誤 @ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class}) public String server500(RuntimeException ex) { System.out.println("500..."); return resultFormat(12, ex); } //棧溢出 @ExceptionHandler({StackOverflowError.class}) public String requestStackOverflow(StackOverflowError ex) { return resultFormat(13, ex); } //其他錯誤 @ExceptionHandler({Exception.class}) public String exception(Exception ex) { return resultFormat(14, ex); } private <T extends Throwable> String resultFormat(Integer code, T ex) { ex.printStackTrace(); log.error(String.format(logExceptionFormat, code, ex.getMessage())); return JsonResult.failed(code, ex.getMessage()); } }
注:
springboot中這樣就可以了!!!
如果是springMVC配置文件中增加下面的配置
<!-- 處理異常 --> <context:component-scan base-package="com.gionee.xo" use-default-filters="false"> <!-- base-package 如果多個,用“,”分隔 --> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" /> <!--控制器增強,使一個Contoller成為全局的異常處理類,類中用@ExceptionHandler方法注解的方法可以處理所有Controller發生的異常--> <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" /> </context:component-scan>
參考:
https://blog.csdn.net/kinginblue/article/details/70186586
https://blog.csdn.net/u014044812/article/details/78219692
https://blog.csdn.net/w372426096/article/details/78429141