使用Spring MVC HandlerExceptionResolver處理異常


轉載 http://fancyboy2050.iteye.com/blog/1300037

最近使用spring mvc開發一個web系統,發現在controller里發生未捕獲異常時不出日志。 


分析DispatcherServlet,初始化handlerExceptionResolvers 
Java代碼    收藏代碼
  1.         /** 
  2.      * Initialize the strategy objects that this servlet uses. 
  3.      * <p>May be overridden in subclasses in order to initialize 
  4.      * further strategy objects. 
  5.      */  
  6.     protected void initStrategies(ApplicationContext context) {  
  7.         initMultipartResolver(context);  
  8.         initLocaleResolver(context);  
  9.         initThemeResolver(context);  
  10.         initHandlerMappings(context);  
  11.         initHandlerAdapters(context);  
  12. // 初始化異常處理支持器  
  13.         initHandlerExceptionResolvers(context);  
  14.         initRequestToViewNameTranslator(context);  
  15.         initViewResolvers(context);  
  16.     }  
  17.   
  18. // 進入初始化處理方法,具體內容就不貼了,主要是先到上下文中搜尋我們自己定義的ExceptionResolvers,如果沒有自定義的resolvers,從默認配置中讀取。  
  19. private void initHandlerExceptionResolvers(ApplicationContext context)  
  20.   
  21. // 從默認策略中取得默認配置,從DispatcherServlet.properties文件中取得相關的配置策略,但是在spring2.5的mvc jar包中properties文件中沒有HandlerExceptionResolver的默認配置,返回一個EmptyList給handlerExceptionResolvers  
  22. protected List getDefaultStrategies(ApplicationContext context, Class strategyInterface)  


分析DispatcherServlet,分發處理請求 
Java代碼    收藏代碼
  1. // 從dispatch方法中看到,系統對請求進行具體的邏輯處理部分被catch住了一次exception,然后會使用servlet持有的ExceptionResolver進行處理  
  2. protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {  
  3.         HttpServletRequest processedRequest = request;  
  4.         HandlerExecutionChain mappedHandler = null;  
  5.         int interceptorIndex = -1;  
  6.   
  7.         // Expose current LocaleResolver and request as LocaleContext.  
  8.         LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();  
  9.         LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable);  
  10.   
  11.         // Expose current RequestAttributes to current thread.  
  12.         RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes();  
  13.         ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);  
  14.         RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable);  
  15.   
  16.         if (logger.isTraceEnabled()) {  
  17.             logger.trace("Bound request context to thread: " + request);  
  18.         }  
  19.           
  20.         try {  
  21.             ModelAndView mv = null;  
  22.             boolean errorView = false;  
  23.   
  24.             try {  
  25.                 processedRequest = checkMultipart(request);  
  26.   
  27.                 // Determine handler for the current request.  
  28.                 mappedHandler = getHandler(processedRequest, false);  
  29.                 if (mappedHandler == null || mappedHandler.getHandler() == null) {  
  30.                     noHandlerFound(processedRequest, response);  
  31.                     return;  
  32.                 }  
  33.   
  34.                 // Apply preHandle methods of registered interceptors.  
  35.                 HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();  
  36.                 if (interceptors != null) {  
  37.                     for (int i = 0; i < interceptors.length; i++) {  
  38.                         HandlerInterceptor interceptor = interceptors[i];  
  39.                         if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {  
  40.                             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);  
  41.                             return;  
  42.                         }  
  43.                         interceptorIndex = i;  
  44.                     }  
  45.                 }  
  46.   
  47.                 // Actually invoke the handler.  
  48.                 HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  
  49.                 mv = ha.handle(processedRequest, response, mappedHandler.getHandler());  
  50.   
  51.                 // Do we need view name translation?  
  52.                 if (mv != null && !mv.hasView()) {  
  53.                     mv.setViewName(getDefaultViewName(request));  
  54.                 }  
  55.   
  56.                 // Apply postHandle methods of registered interceptors.  
  57.                 if (interceptors != null) {  
  58.                     for (int i = interceptors.length - 1; i >= 0; i--) {  
  59.                         HandlerInterceptor interceptor = interceptors[i];  
  60.                         interceptor.postHandle(processedRequest, response, mappedHandler.getHandler(), mv);  
  61.                     }  
  62.                 }  
  63.             }  
  64.             catch (ModelAndViewDefiningException ex) {  
  65.                 logger.debug("ModelAndViewDefiningException encountered", ex);  
  66.                 mv = ex.getModelAndView();  
  67.             }  
  68. // 這里catch住controller拋出的異常,使用持有的ExceptionResolver處理,當沒有配置自己的處理器時,程序會將異常繼續往上拋出,最終交給我們的容器處理  
  69.             catch (Exception ex) {  
  70.                 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);  
  71.                 mv = processHandlerException(processedRequest, response, handler, ex);  
  72.                 errorView = (mv != null);  
  73.             }  
  74.   
  75.             // Did the handler return a view to render?  
  76.             if (mv != null && !mv.wasCleared()) {  
  77.                 render(mv, processedRequest, response);  
  78.                 if (errorView) {  
  79.                     WebUtils.clearErrorRequestAttributes(request);  
  80.                 }  
  81.             }  
  82.             else {  
  83.                 if (logger.isDebugEnabled()) {  
  84.                     logger.debug("Null ModelAndView returned to DispatcherServlet with name '" +  
  85.                             getServletName() + "': assuming HandlerAdapter completed request handling");  
  86.                 }  
  87.             }  
  88.   
  89.             // Trigger after-completion for successful outcome.  
  90.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);  
  91.         }  
  92. // 當沒有配置ExceptionResolver時,異常將到達這里,最終拋出  
  93.         catch (Exception ex) {  
  94.             // Trigger after-completion for thrown exception.  
  95.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  
  96.             throw ex;  
  97.         }  
  98.         catch (Error err) {  
  99.             ServletException ex = new NestedServletException("Handler processing failed", err);  
  100.             // Trigger after-completion for thrown exception.  
  101.             triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, ex);  
  102.             throw ex;  
  103.         }  
  104.   
  105.         finally {  
  106.             // Clean up any resources used by a multipart request.  
  107.             if (processedRequest != request) {  
  108.                 cleanupMultipart(processedRequest);  
  109.             }  
  110.   
  111.             // Reset thread-bound context.  
  112.             RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable);  
  113.             LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable);  
  114.   
  115.             // Clear request attributes.  
  116.             requestAttributes.requestCompleted();  
  117.             if (logger.isTraceEnabled()) {  
  118.                 logger.trace("Cleared thread-bound request context: " + request);  
  119.             }  
  120.         }  
  121.     }  

 

 

轉載:http://fuliang.iteye.com/blog/947191 


Spring 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的源代碼: 
Java代碼    收藏代碼
  1. // Check registerer HandlerExceptionResolvers...  
  2. ModelAndView exMv = null;  
  3. for (Iterator it = this.handlerExceptionResolvers.iterator(); exMv == null && it.hasNext();) {  
  4. HandlerExceptionResolver resolver = (HandlerExceptionResolver) it.next();  
  5. exMv = resolver.resolveException(request, response, handler, ex);  
  6. }  
  7. if (exMv != null) {  
  8. if (logger.isDebugEnabled()) {  
  9. logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);  
  10. }  
  11. WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());  
  12. return exMv;  
  13. }  

可以看到可以插入自己的HandlerExceptionResover來搞定這個問題,我們可以在resolveException方法任意處理異常和log。也可以 
把錯誤信息個性化后傳到view層顯示。 
我們只有簡單的需求,就是把沒有catch的異常記入log,將異常的完整信息放在錯誤頁面的一個隱藏的區域,方便查找出現錯誤的原因。 
首先我們實現HandlerExceptionResolver 
Java代碼    收藏代碼
  1. package com.qunar.advertisement.exception;  
  2.   
  3. import java.util.HashMap;  
  4. import java.util.Map;  
  5.   
  6. import javax.servlet.http.HttpServletRequest;  
  7. import javax.servlet.http.HttpServletResponse;  
  8.   
  9. import org.apache.log4j.Logger;  
  10. import org.springframework.web.servlet.HandlerExceptionResolver;  
  11. import org.springframework.web.servlet.ModelAndView;  
  12.   
  13. import com.qunar.advertisement.utils.StringPrintWriter;  
  14.   
  15. public class QADHandlerExceptionResolver implements HandlerExceptionResolver{  
  16.     private static Logger logger = Logger.getLogger(QADHandlerExceptionResolver.class);  
  17.     @Override  
  18.     public ModelAndView resolveException(HttpServletRequest request,  
  19.             HttpServletResponse response, Object handler, Exception ex) {  
  20.         logger.error("Catch Exception: ",ex);//把漏網的異常信息記入日志  
  21.         Map<String,Object> map = new HashMap<String,Object>();  
  22.         StringPrintWriter strintPrintWriter = new StringPrintWriter();  
  23.         ex.printStackTrace(strintPrintWriter);  
  24.         map.put("errorMsg", strintPrintWriter.getString());//將錯誤信息傳遞給view  
  25.         return new ModelAndView("error",map);  
  26.     }  
  27.   
  28. }  

我們還需要一個輔助的類StringPrintWriter,因為ex.printStackTrace參數只有個PrintWriter類型的,java自帶的StringWriter 
不可用,所以我們需要自己實現一個裝飾器的StringPrintWriter。 
Java代碼    收藏代碼
  1. package com.qunar.advertisement.utils;  
  2.   
  3. import java.io.PrintWriter;  
  4. import java.io.StringWriter;  
  5.   
  6. public class StringPrintWriter extends PrintWriter{  
  7.   
  8.     public StringPrintWriter(){  
  9.         super(new StringWriter());  
  10.     }  
  11.      
  12.     public StringPrintWriter(int initialSize) {  
  13.           super(new StringWriter(initialSize));  
  14.     }  
  15.      
  16.     public String getString() {  
  17.           flush();  
  18.           return ((StringWriter) this.out).toString();  
  19.     }  
  20.      
  21.     @Override  
  22.     public String toString() {  
  23.         return getString();  
  24.     }  
  25. }  

我們只需要在xml中配置一下就可以了: 
Xml代碼    收藏代碼
  1. <bean class="com.qunar.advertisement.exception.QADHandlerExceptionResolver">  
  2. </bean>  

我們在錯誤頁面隱藏區域顯示錯誤信息: 
Html代碼    收藏代碼
  1. <div style="display:none;">  
  2.      <c:out value="${errorMsg}"></c:out>  
  3. </div>  


免責聲明!

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



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