1. 程式人生 > >Spring-Web專案中的異常處理

Spring-Web專案中的異常處理

前言

異常體系在任何計算機語言中都有著重要的分量,但是對於普通開發者來說總是存在著多多少少的疑問:什麼時候使用異常?什麼時候要對異常進行統一處理?該如何對異常進行統一處理?

這裡,我將把我們後臺系統的異常處理機制的演變過程進行闡釋。

分散式處理

大家都知道,在spring-mvc中事務是要切在service層的,也就是當service層丟擲異常時,進行資料庫操作的回滾。其實也就是說,這一層我們不要去自己捕獲異常,異常都由事務機制去攔截和處理,service層有異常只需要丟擲即可。

那麼捕獲異常和處理異常的操作,很自然的就落到了controller層中,所以對於常見的,也就是分散式的處理方式,一般來說異常處理的程式碼是這個樣子的:

//以下僅為示例程式碼
public class UserController{

    @RequestMapping("/updateUserInfo")
    public @ResponseBody String updateUserInfo(UserInfo ui){
        try{
            userService.updateUserInfo(ui)
        }catch(Exception e){
            return "更新失敗";
        }
        return "更新成功";
    }
}

這樣實現的好處就是:很多人寫的異常各自維護,不會相互影響,你改你的,我改我的。缺點也比較明顯:就是很多的try…catch。

這時候,我們很自然的會想到統一處理,如果能夠對異常進行統一處理,那麼,在controller層就可以僅僅處理業務邏輯,不用寫不必要的try…catch塊了。

集中式處理

集中式處理異常有兩種方式:一種是使用aop機制,給所有的controller層方面插入處理程式碼;另一種是使用spring提供的異常處理器機制。這裡的第二種,更好一些,因為在實戰中經常會遇到引數型別轉換錯誤的異常,這是spring級別的異常,使用方法級別的aop機制不能捕獲這些異常。以下說明spring的ExceptionResolver使用方法:

定義ExceptionResolver:

public class
ExceptionResolver extends SimpleMappingExceptionResolver {
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { try { Map<String,Object> result = new HashMap<String,Object>(); result.put("code", 1); //有時候我們不希望直接把後臺異常直接展示給使用者 result.put("data", "請求處理錯誤"); JSONObject jo = JSONObject.fromObject(result); //此行必加,否則返回的json在瀏覽器中看到是亂碼,不易於識別 response.setHeader("Content-Type","text/html;charset=UTF-8"); response.getWriter().write(jo.toString()); response.getWriter().close(); } catch (Exception e) { } return null; } }

spring的配置檔案中新增如下配置項:

    <!-- 異常處理,返回json結果 -->
    <bean id="exceptionResolver" class="你的ExceptionResolver類"></bean>

定義好以後,controller層中所有的try…catch就可以去掉了。是不是很好呢?

在實際中遇到複雜的業務邏輯時,也就是service層方法程式碼邏輯很複雜時,比如:下單、支付、領取優惠券等,根據不同的情況,需要給前端返回不同的錯誤資訊,這個時候一種做法就是返回列舉值,在controller層去識別並返回具體錯誤資訊,另外一種就是直接throw一個異常。

其實,我們只能用第二種方法,因為錯誤時,我們很可能需要讓事務機制起效,想著讓已經操作的一部分結果回滾,所以只能選擇第二種方式來將錯誤資訊放在異常的Message裡面丟擲。

這時候,我們就遇到了另外一個問題,spring級別的異常我們不想把message直接拋給前端,我們自己的異常又要把message拋給前端,這時候怎麼辦呢?

集中-分散式

遇到以上的情況,可以有一種方法處理。就是自定義異常,當controller層遇到自定義異常時,把異常資訊返給前端。

也就是對於上述說到的複雜邏輯業務,在controller層仍然保留try…catch塊,捕獲自定義的異常。

那麼,有沒有,完全集中式的處理方案呢,其實,再前進一步就可以了。

改進型集中式處理

仍然需要自定義異常,當我們的邏輯要丟擲邏輯層面的異常時,建立並丟擲自定義的異常,然後在ExceptionResolver裡對自定義異常進行差異化處理:

定義ExceptionResolver:

public class ExceptionResolver extends SimpleMappingExceptionResolver {

    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {

        try {
            Map<String,Object> result = new HashMap<String,Object>();
            result.put("code", 1);
            if(ex instanceof MyException){
                result.put("data", "異常:"+ex.getMessage());
            }else{
                result.put("data", "請求處理錯誤");
            }
            JSONObject jo = JSONObject.fromObject(result);
            //此行必加,否則返回的json在瀏覽器中看到是亂碼,不易於識別
            response.setHeader("Content-Type","text/html;charset=UTF-8");
            response.getWriter().write(jo.toString());
            response.getWriter().close();
        } catch (Exception e) {
            //e.printStackTrace();
        }
        return null;
    }   
}