Spring MVC 處理異常的3種方式


使用Spring MVC開發的博客網站時,遇到了如何處理業務層拋出的異常的問題,查閱到了spring官方博客-spring MVC中異常的處理,以下將會以登錄模塊為示例。

愚蠢的處理方式

處理異常遵循“早拋出,晚捕獲"的原則,在controller中統一處理異常,調用業務邏輯service時使用try-catch包圍。

 

然而這樣需要每個controller方法中會編寫模版代碼,自然Spring MVC的設計者也會想到這個問題!於是去查閱資料。

優雅的解決方案

Spring MVC提供的3類處理方式,實現在控制層controller的外圍處理異常,大概示意圖如下:

 

  • 基於異常類(自定義),即針對某類異常;
  • 基於控制器(controller),即針對某個控制器;
  • 全局異常處理;

Spring MVC異常處理

LoginController控制器中處理登錄的方法login,代碼如下:

/**
 * 登錄
 * @param username
 * @param password
 * @param session
 * @return
 */
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public LuoblogResult<Author> login(String username, String password, HttpSession session) {
    // 登錄的業務邏輯
    // 當用戶名或密碼錯誤將拋出異常
    Author author = authorService.login(username, password);
    return new LuoblogResult<Author>(true, author);
}

為了和Spring異常處理產生對比,這里不使用任何異常處理,瀏覽器中輸入不存在的用戶,執行結果如下:

 

注:作者名不存在,業務層會拋出BusinessException異常;

1.基於自定異常類處理

是時候讓我們嘗嘗Spring異常處理這個香餑餑,只需要使用@ResponseState注解對自定義異常類進行標注,Spring在處理異常的時候會利用反射對該注解標記的異常特殊處理(個人推測):

/**
 * 業務異常
 * Created by luokaiqiongmou on 2016/12/6.
 */
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "業務異常,偽裝為錯誤請求")
public class BusinessException extends RuntimeException{
    // 一些內容
}

現在再來登錄一次,輸入不存在的用戶名,執行結果如下:

 

巧妙的將原本的500錯誤,修改為400錯誤,那這有什么用了,這樣你就可以控制對應異常發生時轉化為其他的狀態碼,便於前端處理。

2.基於控制器,處理異常

現在我們可以針對某個控制器處理異常,只需要在控制器中增加一個異常處理方法,並使用@ExceptionHandler標注:

    @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "業務異常,針對控制器處理")
    @ExceptionHandler(BusinessException.class)
    public void conflict() {
        // 不做任何事或者可以做任何事
    }

同樣登錄一次,結果如下:

 

注:@ExceptionHandler標注的方法,方法簽名靈活、多變。被@ResponseStatus注解的方法將會修改相應狀態碼,而使用@ResponseBody可以返回json格式的數據,再供前端處理。參考:示例

3. 全局的異常處理

針對某個控制器處理異常的方式會造成代碼入侵

方式一:使用注解

現在創建一個專門處理異常的類,並添加@ControllerAdvice注解,如下:

/**
 * 異常處理
 * Created by luokaiqiongmou on 2016/12/17.
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(BusinessException.class)
    @ResponseBody
    public LuoblogResult<Object> handle(BusinessException e) {
        logger.warn("GloabalExceptionHandler handing a Exception: " + e.getMessage());
        // 業務失敗返回
        return new LuoblogResult<Object>(false, e.getDescription());
    }

}

再次登錄,效果如下:

方式二:實現HandlerExceptionResolver,並注入到spring容器中

創建一個類實現HandlerExceptionResolver,並在配置文件中注入bean,參考:自定義異常處理器,以下方式未做測試

Spring已經為我們預定義了一個處理異常的解析類SimpleMappingExceptionResolve,添加如下配置文件即可:

<bean id="simpleMappingExceptionResolver" class=
     "org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
      <map>
        <!-- key:異常類別(非全稱), 視圖名稱 -->
         <entry key="DatabaseException" value="databaseError"/>
         <entry key="InvalidCreditCardException" value="creditCardError"/>
      </map>
    </property>

    <!-- 默認的錯誤處理頁面,異常的名稱 -->
    <property name="defaultErrorView" value="error"/>
    <property name="exceptionAttribute" value="ex"/>
        
    <!-- Name of logger to use to log exceptions. Unset by default, 
           so logging is disabled unless you set a value. -->
    <property name="warnLogCategory" value="example.MvcLogger"/>
  </bean>

總結

三種異常處理方式,主要涉及3個注解和1個接口,@ExceptionHandler標注的方法被定義為處理指定類型異常;@ResponseStatus標注的方法執行,會修改響應頭中的狀態碼;Spring會把@ControllerAdvice的類內部使用@ExceptionHandler方法應用到所有的 @RequestMapping注解的方法上。

  • 基於異常的處理方式,第一種無法實現json數據返回
  • 基於控制器的方式
  • 全局的方式!


免責聲明!

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



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