從零開始學 Java - Spring MVC 統一異常處理


看到 Exception 這個單詞都心慌

如果有一天你發現好久沒有看到Exception這個單詞了,那你會不會想念她?我是不會的。她如女孩一樣的令人心動又心慌,又或者你已經練功到了孤獨求敗,等了半輩子終於看到了她,這時候你的小弟准備沖上去解決它說:大哥,我來解決它。你擺擺手說:還是我來最后一次吧...

從此,你再也沒看到過Exception了。

異常是個好東西

做開發的對異常在熟悉不過了,幾乎是天天打交道的。記得當年寫代碼的時候記住的第一個異常是未將對象引用設置到對象的實例,現在想想已經好久沒有看到他了,當然這是 .Net 下的一個異常,對應 Java 下的是java.lang.NullPointerException

其實,異常並不可怕,可怕的是你不知道怎么解決它。 解決異常的方式有很多種,比如寫好每一行代碼,保證不出任何邏輯錯誤,就可以從根本上解決問題,但是,沒有一個程序員能保證自己的代碼不報異常,這也就是為什么會有 Bug 這個令人討厭的東西了。當然,如果我們寫的代碼都沒有異常,不出 Bug ,那怎么讓我們有和測試妹子在一起工作的機會呢!哈哈哈...

既然我們保證不了從根本上解決代碼不出異常的情況,那我們是誰阿,我們為了在測試妹子面前展示厲害到無敵的敲代碼能力,我們就偷偷的在業務代碼里使用 try...catch... 來讓她看不出你寫代碼能力不行,你這時候就厲害到不行了,你脈脈看着她,她看着你...

難道不應該有即使我們不使用 try...catch... 也能捕獲寫出來的異常么?

這里有一本《異常秘籍》

並不是說使用 try...catch... 不行,是因為在程序里可能會有你可預知的異常,這時候你當然會去使用它來捕獲異常,但是如果在一個你不可預知的方法里,你本能認為它不會出異常,你還會使用 try...catch... 去捕獲么?如果你回答會,那我問你:你意思就是在每一個方法里加入 try...catch... 代碼了?

當然,這樣並不是不行,是不太好。你想想,這樣寫起來是不是也太累了,我們都很懶的,而且我們程序里有一個講究是「耦合性」,那你這就完全不符合「高內聚、低耦合」咯?簡單說,異常的處理對業務代碼的侵入性太強了,不夠美,我們當然有更好的處理方式了。

我們可不可以統一處理異常呢? 當然可以啦!

現在就新建一個ExceptionHandler.java異常處理類。

@Component
public class ExceptionHandler implements HandlerExceptionResolver {

    private static Logger log = Logger.getLogger(ExceptionHandler.class);

    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {
        log.error("ExceptionHandler 捕獲的異常:", exception);

        String requestType = request.getHeader("X-Requested-With");
        String type = "api";    //TODO:
        if (!type.equals("api") && StrUtil.isNullOrEmpty(requestType)) {
            // 非API請求
            return new ModelAndView("redirect:/500.html");
        } else {// JSON格式返回

            Map<String, Object> responseMap = new HashMap<String, Object>();
            responseMap.put("code", -1);
            responseMap.put("msg", "系統異常,請稍后重試!");
            String json = new Gson().toJson(responseMap);
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            try {
                response.getWriter().write(json);
                response.getWriter().flush();
                return null;
            } catch (IOException e) {
                log.error("", e);
            }
        }
        return new ModelAndView("redirect:/500.html");
    }
}

好,就這樣簡單,完成了異常的統一處理。其實是 Spring 中定義的 HandlerExceptionResolver 接口,我們重寫里邊的 resolveException 方法就可以捕獲項目中未處理的異常。當然,這就要求我們項目中的異常要一層一層的拋出去了,這時候不要擔心,我們會最終捕獲它的。

現在簡單說說上邊我的捕獲代碼的處理方式,首先,我捕獲到異常就寫一個 log 記錄它,以便於我們找出查看,然后,如果是我們的 API 接口請求的話,我就返回接口的統一 Json 格式,如果是其他請求的話,我就會返回到一個 500 的錯誤頁面,以優雅的方式提示用戶。

怎么使用

其實,完全就不用說怎么使用了,非常簡單的用法,mafly.那這里我就試着拋一個異常出去,然后故意不捕獲它,看看結果到底會怎樣?
1.先在 ServiceImp 層拋一個異常。
exception_serviceimp.jpg

2.在 Controller 層調用這個方法。
exception_controller.jpg

3.請求一個 API 接口。
exception_log.jpg

這時候,你看控制台打印出來了異常日志,就是我們剛剛拋出來的,你也可以調試一下,看看執行過程。這個時候,你就可以隨心所欲的統一處理異常了。
文章的具體的案例,都可以訪問我的 Github 看到 https://github.com/mafly/SpringDemo


免責聲明!

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



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