Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 380|回复: 0

使用Spring MVC HandlerExceptionResolver处理异常

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-6-18 13:24:57 | 显示全部楼层 |阅读模式

    转载 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;  
    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;  
    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>  
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-2 01:58 , Processed in 0.081986 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表