一直在使用springmvc来当做后台应用的controller。最近发现一个问题,就是客户端做了一次数据处理,报了500异常,但是再服务端却没有任何异常输出。尽管我做了自定义异常处理:
1 | <bean id="exceptionResolver" class="com.ruoshui.bethune.web.handler.BethuneMappingExceptionResolver"> |
1 | /** |
这让我很困惑,查了半天,发现是spring mvc异常处理的一个漏洞。
spring mvc 异常处理机制
在springmvc的主servlet:DispatcherServlet中定义里异常处理机制:
1 | /** |
会调用shouldApplyTo来查看这个handler是否可以处理这个异常。
在这个handlerExceptionResolvers列中,我们的自定义异常是放在最后面的:

##有些异常被springmvc内部处理,不会再向外暴露
当有spring异常的时候,会优先由spring内部的异常处理handler来筛选(挑剩的才给我们。。。。),而在DefaultHandlerExceptionResolver中
1 |
|
在处理一些异常的时候只是返回错误,但却没有任何log.error级别的输出,只有在log4j中将springmvc的异常级别调成debug,才会有输出。
后来查到我们后台就是在从对象转json的时候有一个空指针异常,被HttpMessageNotWritableException异常封装,所以才会没有日志输出。
1 | 20:50:37,923 DEBUG DefaultHandlerExceptionResolver:134 - Resolving exception from handler [public com.xx.JsonResponse com.xx.xx.putUserDetail(com.xx.xx,org.springframework.validation.BindingResult,javax.servlet.http.HttpServletRequest)]: |
(后来查到我们后台就是在从对象转json的时候有一个空指针异常,被HttpMessageNotWritableException异常封装,所以才会没有日志输出。)
解决方案
虽然可以通过debug来查出异常,但肯定不是我想要的。如何才能在生产环境也能输出异常信息呢?
很简单,只要让我们的自定义exceptionHandler放在handlerExceptionResolvers列的第一位就可以,所以的exceptionHandler都实现了Ordered接口,可以定义优先级,spring默认的几个handler,都比较良心的设置成最低
1 | /** |
我们只要在自定义exceptionHandler的构造函数中,把优先级设置成最高,就可以了。下图是设置好之后DispathServlet处理异常时的顺序

这样所有的异常都由我们监控,输出异常日志也就简单了。