1. 程式人生 > >Spring 梳理 -異常處理

Spring 梳理 -異常處理

  1. Spring 提供了多種方式將異常轉換為相應
    1. Spring框架提供的通用異常,將異常轉換為HTTP狀態碼
      1. Spring預設會將自身丟擲的異常自動對映到合適的狀態碼,如下是一些示例:

        舉個例子,當後端丟擲如下異常(TypeMismatchException異常,往方法傳參時,型別不匹配):

        org.springframework.beans.TypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'long'; nested exception is java.lang.NumberFormatException: For input string: "2l" at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:77) at org.springframework.beans.TypeConverterSupport.convertIfNecessary(TypeConverterSupport.java:47) at org.springframework.validation.DataBinder.convertIfNecessary(DataBinder.java:603) ...

        前臺返回400狀態碼:

    2. 在異常上可以新增@ResponseStatus註解,從而將普通異常轉換為HTTP狀態碼
      1. 除了以上異常,對於其它異常以及我們業務自己丟擲的異常,如果沒有明確繫結Http狀態碼,響應預設都會帶有500狀態碼
      2. 當然,除了這些預設機制,我們也可以將自定義異常繫結特點的Http狀態碼,通過@ResponseStatus註解可實現,如下示例:

         定義一個異常,通過@ResponseStatus註解繫結400狀態碼:

        @ResponseStatus(value = HttpStatus.NOT_FOUND)
        public class MyException extends RuntimeException
        {
        
        }

        然後再controller丟擲自定義異常throw new MyException();

        訪問controller,發現響應確實返回了400狀態碼。

    3. 在方法上新增@ExceptionHandler註解,使得控制器方法具備處理異常的能力
      1. 控制器內部異常,在控制器內部處理
      2. 我們在controller下添加了一個MyException異常的處理方法,直接返回到body。適用於控制器內部,丟擲“MyException.class”的方法。如果控制內部丟擲的有其他型別的異常,如OtherException,再在控制器內部新增一個“@ExceptionHandler(OtherException.class)

        ”即可

            @ExceptionHandler(MyException.class)
            @ResponseBody
            public String handleException(){
                return "handle by ExceptionHandler."; }

        開啟瀏覽器,觀察結果:

    4. 控制器通知(@ControllerAdvice)
      1. 使用者新增異常處理全域性機制,避免在每個控制內內部定義獨立的異常控制流程
      2. @ControllerAdvice最實用定義場景是將所有@ExceptionHandler方法收集到一個類中,這樣所有控制器的異常可以在一個進行一致的處理
      3. 異常處理方法只能處理同一個controller中丟擲的異常,然而一個系統,肯定不止一個controller,總不可能在每個controller中都新增重複性的異常處理方法吧~~

        那麼對於多個controller,如何處理異常呢?使用@ControllerAdvice註解即可。

      4. 有一個點注意下,就是spring 掃描配置的時候,要包括該bean,參考配置如下,可參考:
        1. spring-mvc.xml:
          
              <context:component-scan base-package="com.cetiti.epdc.dss" >
                  <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>  
                  <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/> 
              </context:component-scan> 
          
          spring.xml
          
              <context:component-scan base-package="com.cetiti.epdc.dss">
                  <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
                  <context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
              </context:component-scan>
          
          另外,在上面的示例中,範圍更小的異常,優先順序更大,所以會呼叫handleNumberFormatException方法。

           

      5. 帶有@ControllerAdvice註解的類,可以收到系統中所有Controller丟擲的異常,如下示例:

        複製程式碼
        @ControllerAdvice
        public class DSSExceptionHandler extends BaseController
        {
        
            /** * 處理controller丟擲的異常 * * @return */ @ExceptionHandler(Exception.class) @ResponseBody public String handleException(HttpServletRequest request, Exception e) { logger.error("Request FAILD, URL = {} ", request.getRequestURI()); logger.error(e.toString(), e); return gson.toJson(BaseController.FAILD); } /** * 處理controller丟擲的異常 * * @return */ @ExceptionHandler(NumberFormatException.class) @ResponseBody public String handleNumberFormatException(HttpServletRequest request, NumberFormatException e) { logger.error("Request FAILD, URL = {} ", request.getRequestURI()); logger.error(e.toString(), e); return gson.toJson(BaseController.FAILD); } }
         
  2. 下面是幾個ExceptionHandler註解的使用例子:

    @Controller
    public class ExceptionHandlingController {
    
      // @RequestHandler methods
      ...
      
      // 以下是異常處理方法
      
      // 將DataIntegrityViolationException轉化為Http Status Code為409的響應
      @ResponseStatus(value=HttpStatus.CONFLICT, reason="Data integrity violation")  // 409
      @ExceptionHandler(DataIntegrityViolationException.class)
      public void conflict() {
        // Nothing to do
      }
      
      // 針對SQLException和DataAccessException返回檢視databaseError
      @ExceptionHandler({SQLException.class,DataAccessException.class})
      public String databaseError() {
        // Nothing to do.  Returns the logical view name of an error page, passed to
        // the view-resolver(s) in usual way.
        // Note that the exception is _not_ available to this view (it is not added to
        // the model) but see "Extending ExceptionHandlerExceptionResolver" below.
        return "databaseError";
      }
    
      // 建立ModleAndView,將異常和請求的資訊放入到Model中,指定檢視名字,並返回該ModleAndView
      @ExceptionHandler(Exception.class)
      public ModelAndView handleError(HttpServletRequest req, Exception exception) {
        logger.error("Request: " + req.getRequestURL() + " raised " + exception);
    
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", exception);
        mav.addObject("url", req.getRequestURL());
        mav.setViewName("error");
        return mav;
      }
    }

     

  3. 參考 https://www.cnblogs.com/junzi2099/p/7840294.html
  4. 參考 https://www.cnblogs.com/chenpi/p/6117090.html