針對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>