springMVC全局异常处理框架@ExceptionHandler上线后隔一段时间失效

有用过springMVC的人都知道,springMVC提供了非常方便的异常处理机制。只需要一个注解,就可以自动拦截所有异常,并根据自己的需要输出ModelView或者json异常信息。


springMVC全局异常处理框架@ExceptionHandler上线后隔一段时间失效_第1张图片

如图,@ControllerAdvice注解用于拦截所有controller中的异常,@ExceptionHandler定义了异常处理的方法,@ResponseBody自动将异常信息处理成json返回,具体可以看http://www.jianshu.com/p/dff00fce79d2

原本正常的异常日志是这样紫的


可以看到,系统中抛出的BusinessException异常首先经过ExceptionHandlerExceptionResolver处理后,通过动态加载的方式,由BusinessExceptionHandler中的handleBusinessException方法处理了,本来都是正常的,但是在服务器运行一段时间后就失效了,转由springmvc默认的异常处理类ResponseStatusExceptionResolver处理,日志如下


这是很诡异的问题,本地又无法复现,排查起来非常棘手。

于是我们试着了解一下springmvc的异常处理机制,原来,springmvc提供了五个异常处理器,AbstractHandlerMethodExceptionResolver,DefaultHandlerExceptionResolver,ExceptionHandlerExceptionResolver,ResponseStatusExceptionResolver, SimpleMappingExceptionResolver, 这几个异常处理器都继承了抽象类AbstractHandlerExceptionResolver, 最终实现了HandlerExceptionResolver和Ordered接口,Ordered用于对这几个处理器排序,决定先使用哪个处理器,如果找不到该处理器,则会按照顺序寻找下一个可用的处理器。

加上这个bug是每次都在服务器运行一段时间之后才出现的,于是我们大致可以猜出来,这是由于springmvc用了某个缓存,缓存丢失,才使得ExceptionHandlerExceptionResolver失效,于是springmvc才按照顺序寻找下一个处理器,直到找到了ResponseStatusExceptionResolver。

于是我们开始阅读源码,从日志信息可以看到,异常处理的入口在ExceptionHandlerExceptionResolver的133行, 并且打印出了“Resolving exception from handler”的信息,于是定位到他的父类AbstractHandlerExceptionResolver的resolveException方法


springMVC全局异常处理框架@ExceptionHandler上线后隔一段时间失效_第2张图片

springmvc的dispacherServlet对所有的异常进行了全局捕获,然后执行

DispatcherServlet的processHandlerException方法处理异常。


springMVC全局异常处理框架@ExceptionHandler上线后隔一段时间失效_第3张图片

这里就可以看到异常处理的逻辑了,spring启动的时候,会把所有实现了HandlerExceptionResolver接口的异常处理器按照Ordered接口的顺序放入到handlerExceptionResolvers容器中,处理的时候从容器中依次取出来执行resolveException方法,知道返回不为空

到这里可以判定,上图中的result应该是null值,继续跟踪代码发现,只有可能是ExceptionHandlerExceptionResolver的getExceptionHandlerMethod返回一个null值了


springMVC全局异常处理框架@ExceptionHandler上线后隔一段时间失效_第4张图片

这里可以看到ExceptionHandlerExceptionResolver用了一个exceptionHandlerAdviceCache来存放所有使用@ExceptionHandler注解的方法,

于是印证了我们之前关于缓存被清空的猜想。

那么关键是为什么服务器运行一段时间之后exceptionHandlerAdviceCache中的缓存会被清空了,我们继续查看源码


springMVC全局异常处理框架@ExceptionHandler上线后隔一段时间失效_第5张图片

原来,在ExceptionHandlerMethodResolver的getMappedMethod方法中会将捕获到的异常跟我们在BusinessException的注解@Exception异常对比,只要捕获到的异常是@Exception注解的异常的子类子接口,就会将相应的处理方法放入exceptionHandlerAdviceCache。

因此这里exceptionHandlerAdviceCache会被清空只有一种可能,捕获到的异常不是我们定义的任何一个异常的子类,我们定义的异常有BusinessException和Exception,这里我犯了一个常识性的错误,误将Exception作为所有异常的根,因此有一些继承Throwable的异常无法捕获,才导致这个问题

至此,问题排查结束,增加捕获Throwable即可

你可能感兴趣的:(springMVC全局异常处理框架@ExceptionHandler上线后隔一段时间失效)