樓主前幾天寫了一篇“Java子線程中的異常處理(通用)”文章,介紹了在多線程環境下3種通用的異常處理方法。
但是平時大家的工作一般是基於開發框架進行的(比如Spring MVC,或Spring Boot),所以會有相應特定的異常處理方法,這篇文章要介紹的就是web應用中的異常處理。
想快速解決問題的小伙伴可以只看“解決辦法”,想進一步了解細節的小伙伴還可以看“深入剖析”部分。
適用場景
使用Spring MVC或Spring Boot框架搭建的web應用
解決辦法
@ControllerAdvice注解 + @ExceptionHandler注解
實現一個異常處理類,在類上應用@ControllerAdvice注解,並在異常處理方法上應用@ExceptionHandler注解。那么在web應用中,當Controller的@RequestMapping方法拋出指定的異常類型時,@ExceptionHandler修飾的異常處理方法就會執行。
示例:
1 @ControllerAdvice 2 public class WebServerExceptionHandler { 3 Logger log = LoggerFactory.getLogger(this.getClass()); 4 5 public WebServerExceptionHandler() { 6 } 7 8 // 指定捕獲的異常類型,這里是自定義的SomeException 9 @ExceptionHandler({SomeException.class}) 10 public ResponseEntity<WebServerExceptionResponse> handle(HttpServletResponse response, SomeException ex) { 11 WebServerExceptionResponse body = new WebServerExceptionResponse(); 12 body.setStatus(ex.getStatus()); 13 body.setMessage(ex.getMessage()); 14 this.log.info("handle SomeException, status:{}, message:{}", new Object[]{body.getStatus(), body.getMessage()}); 15 return new ResponseEntity(body, HttpStatus.valueOf(ex.getStatus())); 16 } 17 18 // 指定捕獲的異常類型,這里是自定義的OtherException 19 @ExceptionHandler({OtherException.class}) 20 public ResponseEntity<WebServerExceptionResponse> handle(HttpServletResponse response, OtherException ex) { 21 WebServerExceptionResponse body = new WebServerExceptionResponse(); 22 body.setStatus(ex.getStatus()); 23 body.setMessage(ex.getMessage()); 24 this.log.info("handle OtherException, status:{}, message:{}", new Object[]{body.getStatus(), body.getMessage()}); 25 return new ResponseEntity(body, HttpStatus.valueOf(ex.getStatus())); 26 } 27 }
深入剖析
@ControllerAdvice的定義如下:
1 @Target(ElementType.TYPE) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 @Component 5 public @interface ControllerAdvice { 6 7 String[] value() default {}; 8 9 String[] basePackages() default {}; 10 11 Class<?>[] basePackageClasses() default {}; 12 13 Class<?>[] assignableTypes() default {}; 14 15 Class<? extends Annotation>[] annotations() default {}; 16 17 }
可以看出它應用在TYPE類型的元素上(也即class或interface),運行時生效。
作用是Controller類的幫助注解,一般搭配@ExceptionHandler注解,用來處理Controller的@RequestMapping修飾的方法拋出的異常。
樓主根據源碼的注釋整理了5個參數的含義,它們都是用來限定需要處理的Controller的:
-
value():等同於basePackages,表示需要被處理的Controller包名數組,例如 @ControllerAdvice("org.my.pkg")。如果不指定,就代表處理所有的Controller類
-
basePackages():表示需要被處理的Controller包名數組,例如 @ControllerAdvice(basePackages={"org.my.pkg","org.my.other.pkg"})
-
basePackageClasses():通過標記類來指定Controller包名數組
-
assignableTypes():通過類的Class對象來指定Controller包名數組
-
annotations():被注解修飾的Controller需要被處理
性能考慮:不要指定過多的參數和異常處理策略,因為異常檢查和處理都是在運行時做的。
@ExceptionHandler的定義如下:
1 @Target(ElementType.METHOD) 2 @Retention(RetentionPolicy.RUNTIME) 3 @Documented 4 public @interface ExceptionHandler { 5 6 /** 7 * Exceptions handled by the annotated method. If empty, will default to any 8 * exceptions listed in the method argument list. 9 */ 10 Class<? extends Throwable>[] value() default {}; 11 12 }
可以看出它作用在方法上面,而且參數很好理解,就是需要處理的異常類的Class對象數組。
但是,它對修飾的異常處理方法的參數和返回值有限定,樓主根據源碼的注釋整理如下:
(1)異常處理方法的參數限定,可以是以下類型,順序任意:
- 異常類對象
-
HttpServletRequest、HttpServletResponse
-
HttpSession
-
InputStream/Reader、OutputStream/Writer
(2)異常處理方法的返回值限定,最終會寫入response流:
-
ResponseEntity
-
HttpServletResponse
-
ModelAndView
-
Model
-
Map
-
View
總結
以上就是在Spring web應用中的異常處理方法:使用@ControllerAdvice搭配@ExceptionHandler修飾自定義異常處理方法,處理來自Controller類中的@RequestMapping方法拋出的異常。
使用時需要根據實際情況,合理設置@ControllerAdvice和@ExceptionHandler的參數。