通常在項目中都會使用到全局異常處理,但是如果添加有攔截器,對攔截器中的異常進行捕獲的時候,就會發現全局異常處理失效,無法對攔截器的異常進行捕獲。
全局異常不能捕獲攔截器異常的原因
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。