• 使用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>  
  • 相关阅读:
    $this是什么意思-成员变量和局部变量的调用
    神经网络 ML08 c-d-e
    机器学习笔记 ML01c
    虚函数
    C++有哪几种情况只能用初始化列表,而不能用赋值?
    C++ 的 I/O
    引用
    宏定义 #define 和常量 const 的区别
    怎么设置才能让外网ip可以访问mysql数据库[转]
    大师的框架面试总结[转]
  • 原文地址:https://www.cnblogs.com/chenying99/p/2712587.html
Copyright © 2020-2023  润新知