1. 程式人生 > >針對400錯誤使用Spring MVC HandlerExceptionResolver處理異常

針對400錯誤使用Spring MVC HandlerExceptionResolver處理異常

昨天晚上一個action設定了大物件,但是提交一直提交不過去,後臺也沒有任何報錯,最後在網上搜索了,使用HandlerException才捕獲到此錯誤,具體文章如下:

pring MVC的確很強大,在每一個你想的到和想不到的地方都會留下鉤子,來插入自定義的實現,透明替換預設實現,
攔截器堆疊結構設計的非常強大,多種試圖的解析,url mapping的多種實現,Locale resolver、Theme resolver
、multipart file resolver,Excepiton hanlder Resolver等等,能讓Spring MVC從1.0到3.0經歷巨大變化,
仍能向後相容,並支援很酷的RESTful風格和強大的簡化xml配置的註解。
這些功能我們在專案中經常用到,但是Excepiton hanlder Resolver可能是個生僻一點的東東,因為我們通常對錯誤
的處理通常不是非常的複雜,很多情況下只是根據異常或者http error code跳轉到錯誤頁面,這個是JSP/servlet就可
以搞定,在web.xml配置一下即可。

今天遇到一個事情,讓我想用到HandlerExceptionResolver這個東東來處理異常。今天準備把自助系統進入上線狀態,
所以把log的級別從DEBUG調到INFO,結果沒有catch的Runtime異常在log記錄,後來跟蹤了一下原來Spring把異常處理的log,
直接使用的是debug,而不是error,所以log級別設定為INFO導致異常沒有記錄,看了一下spring的原始碼:

// Check registerer HandlerExceptionResolvers...  
ModelAndView exMv = null;  
for (Iterator it
= this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) { HandlerExceptionResolver resolver = (HandlerExceptionResolver) it.next(); exMv = resolver.resolveException(request, response, handler, ex); } if (exMv != null) { if (logger.isDebugEnabled()) { logger.debug("Handler execution resulted in exception - forwarding to resolved error view: "
+ exMv, ex); } WebUtils.exposeErrorRequestAttributes(request, ex, getServletName()); return exMv; }

可以看到可以插入自己的HandlerExceptionResover來搞定這個問題,我們可以在resolveException方法任意處理異常和log。也可以
把錯誤資訊個性化後傳到view層顯示。
我們只有簡單的需求,就是把沒有catch的異常記入log,將異常的完整資訊放在錯誤頁面的一個隱藏的區域,方便查找出現錯誤的原因。
首先我們實現HandlerExceptionResolver

package com.qunar.advertisement.exception;  

import java.util.HashMap;  
import java.util.Map;  

import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  

import org.apache.log4j.Logger;  
import org.springframework.web.servlet.HandlerExceptionResolver;  
import org.springframework.web.servlet.ModelAndView;  

import com.qunar.advertisement.utils.StringPrintWriter;  

public class QADHandlerExceptionResolver implements HandlerExceptionResolver{  
    private static Logger logger = Logger.getLogger(QADHandlerExceptionResolver.class);  
    @Override  
    public ModelAndView resolveException(HttpServletRequest request,  
            HttpServletResponse response, Object handler, Exception ex) {  
        logger.error("Catch Exception: ",ex);//把漏網的異常資訊記入日誌  
        Map<String,Object> map = new HashMap<String,Object>();  
        StringPrintWriter strintPrintWriter = new StringPrintWriter();  
        ex.printStackTrace(strintPrintWriter);  
        map.put("errorMsg", strintPrintWriter.getString());//將錯誤資訊傳遞給view  
        return new ModelAndView("error",map);  
    }  

}  

我們還需要一個輔助的類StringPrintWriter,因為ex.printStackTrace引數只有個PrintWriter型別的,java自帶的StringWriter
不可用,所以我們需要自己實現一個裝飾器的StringPrintWriter。

package com.qunar.advertisement.utils;  

import java.io.PrintWriter;  
import java.io.StringWriter;  

public class StringPrintWriter extends PrintWriter{  

    public StringPrintWriter(){  
        super(new StringWriter());  
    }  

    public StringPrintWriter(int initialSize) {  
          super(new StringWriter(initialSize));  
    }  

    public String getString() {  
          flush();  
          return ((StringWriter) this.out).toString();  
    }  

    @Override  
    public String toString() {  
        return getString();  
    }  
}  

我們只需要在spring.xml中配置一下就可以了:

<bean class="com.qunar.advertisement.exception.QADHandlerExceptionResolver">  
</bean>  

我們在錯誤頁面隱藏區域顯示錯誤資訊:

<div style="display:none;">  
     <c:out value="${errorMsg}"></c:out>  
</div>