十四、springboot全局處理異常(@ControllerAdvice + @ExceptionHandler)


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

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM