通常在項目中都會使用到全局異常處理,但是如果添加有攔截器,對攔截器中的異常進行捕獲的時候,就會發現全局異常處理失效,無法對攔截器的異常進行捕獲。
全局異常不能捕獲攔截器異常的原因
SpringBoot下全局異常處理的幾種方式:
- BasicExceptionController——SpringBoot默認處理異常方式,用於異常跳轉到/error,可實現自定義錯誤頁面請求。
- @ExceptionHandle注解——只能在控制器中定義異常處理方法。
- @ControllerAdvice+@ExceptionHandler——增強控制前Controller實現異常攔截。
- SimpleMappingExceptionResolver——攔截異常跳轉到error頁面。
- HandlerExceptionResolver——實現HandlerExceptionResolver攔截異常。
上面幾種方式只能攔截到控制層的異常,而Filter在Controller之前,Controller層的異常捕獲,是無法捕獲到還沒有請求到Controller時發生的異常的。
實現對Filter異常的捕獲
從上面的幾種異常處理方式可以發現,如果要捕獲Filter異常,只能通過控制器層定義的全局異常處理來捕獲;那么也就只能想辦法讓Filter中的異常發送到Controller,再由Controller拋出異常,最后由全局異常捕獲。
有了上面的思路,第一個要解決的問題就是怎么讓過濾器中的異常在Filter中被捕獲到再發送出去。
Filter的實現方式是責任鏈,第一個Filter處理之后,調用第二個Filter,依次往后,直到Filter全部處理完成后結束;當某一個Filter處理中斷,則依次返回結果經過前一個Filter,直到經過第一個Filter過濾后結束過濾責任鏈。
根據Filter過濾的特點,只需要在業務過濾器之前加上用於處理其他過濾器異常捕獲的Filter就可以實現對過濾器異常的處理。
1 簡單實現異常過濾Filter代碼如下:
@Slf4j @Component public class ExceptionFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { chain.doFilter(request, response); } catch (Exception e) { // 異常捕獲,發送到error controller request.setAttribute("filter.error", e); //將異常分發到/error/exthrow控制器 request.getRequestDispatcher("/error/exthrow").forward(request, response); } } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }
2 注冊過濾器時,ExceptionFilter排序要再其他過濾器之前
@Bean public FilterRegistrationBean exceptionFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(exceptionFilter); registration.setName("exceptionFilter"); //此處盡量小,要比其他Filter靠前 registration.setOrder(-1); return registration; }
3 實現Controller接收過過濾器發來的異常
@RestController public class ErrorController { /** * 重新拋出異常 */ @RequestMapping("/error/exthrow") public void rethrow(HttpServletRequest request) { throw ((Exception) request.getAttribute("filter.error")); } }
以上只是Demo示例,在實際使用中,攔截Filter盡量攔截具體的Exception。