javascript
Spring中Controller层、Filter层、Interceptor层全局统一异常处理
Controller層、Filter層、Interceptor層全局統一異常處理
SpringBoot為異常處理提供了很多優秀的方法,但是像我這種新手在處理異常時還是會覺得一頭包,終于我痛定思痛,總結了一下統一異常處理的辦法,讓異常處理變得上流。
本片文章較長,我先定義以下后面會用到的類
/*** @author ht113* * 錯誤dto,就是從后端返回到前端的錯誤信息,下面這個作為例子比較簡單*/ @Data public class ErrorInfo {String datetime;String msg;String url;HttpStatus httpStatus;public ErrorInfo(String datetime, String message, String url, HttpStatus httpStatus) {this.datetime = datetime;this.msg = message;this.url = url;this.httpStatus = httpStatus;} }自定義抽象異常類
/*** @author ht113*** 基本抽象類,所以我自定義的異常類都繼承自此方法*/ public abstract class AbstractBaseException extends Exception{private ErrorInfo errorInfo;public AbstractBaseException(String msg){super(msg);}public AbstractBaseException(String msg, Throwable cause){super(msg, cause);}public void setErrorInfo(ErrorInfo errorInfo){this.errorInfo = errorInfo;}//這是唯一的抽象方法,也是子類必須實現的方法,以此達到使自定義異常對應到不同的http狀態@NonNullpublic abstract HttpStatus getHttpStatus();}以下兩個是自定義的異常類,對應的http狀態分別為NOT_FOUND和INTERNAL_SERVER_ERROR,即404和500
public class MyExceptionA extends AbstractBaseException {public MyExceptionA(String msg) {super(msg);}public MyExceptionA(String msg, Throwable cause){super(msg, cause);}@Overridepublic HttpStatus getHttpStatus() {return HttpStatus.NOT_FOUND;} }public class MyExceptionB extends AbstractBaseException {public MyExceptionB(String msg) {super(msg);}public MyExceptionB(String msg, Throwable cause){super(msg, cause);}@Overridepublic HttpStatus getHttpStatus() {return HttpStatus.INTERNAL_SERVER_ERROR;} }1.一般Controller層的統一異常處理
@ExceptionHandler
在Spring中,我們可以通過對Controller中的方法添加@ExceptionHandler注釋來實現對同一個Controller中路由方法拋出的異常進行處理,來達到我們想要的異常處理效果
/*** @author ht113*/ @RestController public class HelloController {@RequestMapping("/")public String hello() throws AbstractBaseException{throw new MyExceptionA("exception A test");}//需要捕獲的異常的類@ExceptionHandler(MyExceptionA.class)public String exceptionHandlerA(){return "exception A caught successfully";}}處理效果:
是個好方法,但是有個致命缺點,就是處理方法不能在Controller間共享,如果我在另一個Controller中拋出自定義異常就要重新實現一次,太拉垮了。
/*** @author ht113*/ @RestController public class HelloController2 {@RequestMapping("/hello2")public String hello2() throws AbstractBaseException {throw new MyExceptionA("exception A test");}}@ControllerAdvice
通過對Controller添加注釋@ControllerAdvice可以實現Controller層的全局異常處理,處理方法和@ExceptionHandler相同,但是可以在Controller中共享,實例如下:
/*** @author ht113*/ @ControllerAdvice public class MyHandler {@ExceptionHandler(AbstractBaseException.class)public ResponseEntity<ErrorInfo> handler(HttpServletRequest request, AbstractBaseException e){return ResponseEntity.status(e.getHttpStatus()).body(new ErrorInfo(new Date().toString(), e.getMessage(),request.getRequestURL().toString(), e.getHttpStatus()));}}測試類
/*** @author ht113*/ @RestController public class HelloController {@RequestMapping("/1/testA")public void hello() throws AbstractBaseException{throw new MyExceptionA("exception A test from Controller1");}@RequestMapping("/1/testB")public void hello1() throws AbstractBaseException{throw new MyExceptionB("exception B test from Controller1");}}/*** @author ht113*/ @RestController public class HelloController2 {@RequestMapping("/2/testA")public void hello() throws AbstractBaseException {throw new MyExceptionA("exception A test from Controller2");}@RequestMapping("/2/testB")public void hello1() throws AbstractBaseException{throw new MyExceptionB("exception B test from Controller2");}}測試結果
我們所有的自定義異常,只要繼承自AbstractBaseException都可以被hander()方法捕獲,進行統一異常處理,當然也可以根據異常的不同進行特定的處理,只需要在MyHandler類中添加新的處理方法即可
2.Filter層、Interceptor層異常統一處理
過濾器Filter和過濾器Interceptor攔截器的使用總是后端開發不可避免的一部分,有時候我們需要在過濾器或攔截器中進行邏輯處理,拋出異常,那么這里拋出的異常可以被@ControllerAdvice注釋的異常處理類捕獲嗎?答案是Filter不會,而Interceptor可以,Filter是基于Servlet框架編寫的,Interceptor是基于SpringMVC框架的,SpingMVC不會對Filter中的異常統一處理,所以這里被拋出的異常不能被捕獲,那我們如何讓不能被捕獲的異常被被捕獲呢?
答案有兩個:
本例選擇方法2,便于實現真正的統一。
Filter、Interception、Controller處理流程:
用上面提到的testA進行測試,結果如下(如果Controller中拋出異常,那么postHandler不會調用):
HandlerExceptionResolver類圖:
如何使用HandlerExceptionResolver?
Filter中捕獲異常
/*** @author ht113*/ @Component public class MyFilter extends OncePerRequestFilter {@Autowired@Qualifier("handlerExceptionResolver")private HandlerExceptionResolver handlerExceptionResolver;@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {System.out.println("==============i'm in filter===================");handlerExceptionResolver.resolveException(httpServletRequest, httpServletResponse,null,new MyExceptionA("exception A test in filter"));return;//捕獲異常后就直接推出了//filterChain.doFilter(httpServletRequest, httpServletResponse);//System.out.println("===============i'm out filter===================");} }用的是MyExceptionA,結果如下
Interceptor中捕獲異常
/*** @author ht113*/ public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("=============i'm in interceptor============");throw new MyExceptionA("exception A test in interceptor");//return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("=============i'm out interceptor============");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("=============i'm out interceptor============");} }由于ExceptionHandler可以直接捕獲Interceptor中的異常,所以在Interceptor中自定義異常和Controller中一樣使用就可以。
由于ExceptionHandler可以直接捕獲Interceptor中的異常,所以在Interceptor中自定義異常和Controller中一樣使用就可以。
OVER!
總結
以上是生活随笔為你收集整理的Spring中Controller层、Filter层、Interceptor层全局统一异常处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Servlet架构初解析
- 下一篇: 二叉树相关性质以及数学证明