Spring 中異常處理的各類姿式

1. 前言

統一的異常處理對於應用的重要性不言而喻。今天咱們來介紹一下 Spring 如何來進行統一的 Rest 異常處理。同時咱們也會簡單比較一下它們之間的優劣。html

2. @Controller 結合 @ExceptionHandler

在控制器中聲明一個方法而後用 @ExceptionHandler 註解標記便可:web

@Controller
 @RequestMapping("/test")
 public class TestController {
  
     @RequestMapping("/err")
     @ResponseBody
     public Object demo1(){
         int i = 1 / 0;
         return new Date();
     }
  
     @ExceptionHandler({RuntimeException.class})
     public ModelAndView fix(Exception ex){
         System.out.println(ex.getMessage());
         return new ModelAndView("error",new ModelMap("ex",ex.getMessage()));
     }
 }複製代碼

優勢:編程

  • 優先級最高。
  • @ExceptionHandler 標記的方法返回值類型支持多種。能夠是視圖,也能夠是 json 等。

缺點:json

  • 一個 Controller 中的 @ExceptionHandler 註解上的異常類型不能出現相同的,不然運行時拋異常。
  • 須要顯式的聲明處理的異常類型。
  • 做用域僅僅是該 Controller 並非真正意義上的全局異常。若是要想做用於全局須要將其放入全部控制器的父類中。

3. @ControllerAdvice 結合 @ExceptionHandler

這是 2. 的改進型,經過定義 @ControllerAdvice 類並在方法上標記 @ExceptionHandler ,達到了全局異常處理的目的:app

@ControllerAdvice
 public class TestController {
 
  
     @ExceptionHandler({RuntimeException.class})
     public ModelAndView fix(Exception ex){
         System.out.println(ex.getMessage());
         return new ModelAndView("error",new ModelMap("ex",ex.getMessage()));
     }
 }複製代碼

優勢:框架

  • 全局的異常處理。
  • 徹底控制響應的主體以及狀態碼
  • 將多個異常映射到同一方法,以一塊兒處理,而且它充分利用了更新的 Restful ResponseEntity 響應

缺點:ide

  • 一個 Controller 中的 @ExceptionHandler 註解上的異常類型不能出現相同的,不然運行時拋異常。
  • 須要顯式的聲明處理的異常類型。

通常狀況下也建議使用該方式進行異常處理。大多數狀況下都是兼容的。編碼

4. HandlerExceptionResolver 接口

實現 HandlerExceptionResolver 接口,這裏咱們繼承其抽象實現 AbstractHandlerExceptionResolver :spa

@Component
 public class RestResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver {
  
     @Override
     protected ModelAndView doResolveException(
       HttpServletRequest request, 
       HttpServletResponse response, 
       Object handler, 
       Exception ex) {
         try {
             if (ex instanceof IllegalArgumentException) {
                 return handleIllegalArgument((IllegalArgumentException) ex, response, handler);
             }
            //todo more exception
         } catch (Exception handlerException) {
               //todo 
         }
         return null;
     }
  
     private ModelAndView 
       handleIllegalArgument(IllegalArgumentException ex, HttpServletResponse response) 
       throws IOException {
         response.sendError(HttpServletResponse.SC_CONFLICT);
         String accept = request.getHeader(HttpHeaders.ACCEPT);
           //todo  more  response
         return new ModelAndView();
     }
 }複製代碼

優勢:code

  • 這是一個全局的異常處理器。
  • 這種方式全局異常處理返回JSPvelocity 等模板視圖比較方便。
  • 支持多種格式的響應,雖然覆寫的方法返回的是 ModelAndView 可是由於參數中有 HttpServletResponse, 咱們能夠利用它來進行定製響應結果。例如,若是客戶端要求輸入application / json,那麼在出現錯誤狀況時,咱們要確保咱們返回一個以application / json編碼的響應。

缺點:

  • 咱們須要與低級的 HttpServletResponse 交互才能實現各類形式的響應體。
  • 優先級比較低

5. Spring Boot 中的異常處理

若是你用的框架是 Spring Boot 。 咱們還能夠用它獨特的處理方式。優勢是屏蔽了低級的API,缺點也比較明顯,沒法捕捉到具體的異常。

5.1 實現 ErrorController

Spring Boot 在默認狀況下,提供了 /error 映射來處理全部錯誤,在 Servlet 容器裏註冊了全局的錯誤頁面(Whitelabel Error Page)並返回客戶端。經過實現 ErrorController 接口並註冊爲 Bean。這裏再也不舉例。可參考 BasicErrorController

5.2 添加 ErrorAttributes

咱們也能夠添加 ErrorAttributes 類型的 Bean 來替換替換默認的異常處理。

@Component
 public class MyCustomErrorAttributes extends DefaultErrorAttributes {
  
     @Override
     public Map<String, Object> getErrorAttributes(
       WebRequest webRequest, boolean includeStackTrace) {
         Map<String, Object> errorAttributes = 
           super.getErrorAttributes(webRequest, includeStackTrace);
         errorAttributes.put("locale", webRequest.getLocale()
             .toString());
         errorAttributes.remove("error");
  
         //todo your business
  
         return errorAttributes;
     }
 }複製代碼

5.3 繼承基類 BasicErrorController

Spring Boot 自動配置還提供了實現 ErrorController 接口異常處理的基類 BasicErrorController,默認是處理 text/html類型請求的錯誤,能夠繼承該基類自定義處理更多的請求類型,添加公共方法並使用 @RequestMapping 註解的 produce屬性指定處理類型。

@Component
 public class MyErrorController extends BasicErrorController {
  
     public MyErrorController(ErrorAttributes errorAttributes) {
         super(errorAttributes, new ErrorProperties());
     }
  
     @RequestMapping(produces = MediaType.APPLICATION_XML_VALUE)
     public ResponseEntity<Map<String, Object>> xmlError(HttpServletRequest request) {
          
     //todo your business
  
     }
 }複製代碼

6. Spring 5 的 ResponseStatusException

另外在最新的 Spring 5 中你還能夠經過 拋出 ResponseStatusException 異常來進行處理。

好處:

  • 使用比較方便
  • 一種類型,多種狀態代碼:一種異常類型能夠致使多種不一樣的響應。與@ExceptionHandler相比,這減小了緊密耦合
  • 咱們將沒必要建立那麼多的自定義異常類
  • 因爲能夠經過編程方式建立異常,所以能夠更好地控制異常處理

缺點:

  • 沒有統一的異常處理方式,強制執行某些應用程序範圍的約定更加困難
  • 可能會有大量的重複代碼。

7. 總結

咱們對經常使用的、不經常使用的 Spring 處理異常的方式進行了總結和優劣上的分析。 相信你能夠從中找到適合你的處理方式。若是對你有用請幫忙點一個贊,您的鼓勵,個人動力!

關注公衆號:Felordcn獲取更多資訊

我的博客:https://felord.cn

相關文章
相關標籤/搜索