Spring全局异常处理
1. 概述
在 Java Web 系统开发中,不管是 Controller 层、Service 层还是 Dao 层,都有可能抛出异常。如果在每个方法中加上各种 try catch 的异常处理代码,那样会使代码非常繁琐。在Spring
中,我们可以将所有类型的异常处理从各个单独的方法中解耦出来,进行异常信息的统一处理和维护。
在 Spring MVC 中全局异常捕获处理的解决方案通常有两种方式:
- 使用
@ControllerAdvice
+@ExceptionHandler
注解进行 Controller 层异常处理。
使用注解方式,只能处理控制器中的异常 。 - 实现
org.springframework.webb.servlet.HandlerExceptionResolver 接口中的 resolveException
方法。
由于HandlerExceptionResolver很少使用,因此这里只介绍第一种实现方式。
2. 使用 @ControllerAdvice + @ExceptionHandler 注解
这种方式的好处是简单统一,但是需要在每一层都要抛出异常,保证异常能够被controller层接收。
2.1 @ControllerAdvice注解
@ControllerAdivce
的组成如下:
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
@ControllerAdvice
注解用于定义全局异常处理类。类中可定义多个异常处理函数,可分别处理不同的异常情况。其参数用于指定生效的范围。形式可以是包、class、注解等,默认是对全部的controller生效。
2.2 @ExceptionHandler注解
@ExceptionHandler()
注解用于定义当前方法捕获处理的异常类型。当在 controller 中捕获到相应的异常类型时,就会触发当前方法的执行,进行异常处理。
被@ExceptionHandler()
注解修饰的方法,一般会将该异常类型作为参数传入到方法中,从而在方法中获取异常信息并进一步处理。
@ExceptionHandler(CustomException.class)
public Result exception(CustomException e){
return new Result(false,"全局异常处理");
}
如果@ExceptionHandler所在的类是@Controller,则此方法只作用在此类。如果@ExceptionHandler所在的类带有@ControllerAdvice注解,则此方法会作用在全局。
2.3 特点
使用@ControllerAdvice
和@ExceptionHandler
注解,能够细粒度的控制该异常处理器针对哪些 Controller 有效:
- 一个系统里就能够存在不同的异常处理器,Controller 也可以有选择的决定使用哪个,更加灵活。
- 不同的业务模块可能对异常处理的方式不同,通过该机制就能做到。
- 设想一个一开始并未使用全局异常处理的系统,如果直接引入全局范围内生效的全局异常处理,势必可能会改变已有 Controller 的行为,有侵入性。
- 也就是说,如果不控制生效范围,即默认对所有 Controller 生效。如果控制生效范围,则默认对所有 Controller 不生效,降低侵入性。
一个小案例如下:
package top.xcyxiaoxiang.common;
/**
* @Description: 全局异常处理案例
* @date: 2022/6/30 下午9:49
* @author: xcy.小相
* @ClassName : GlobalExceptionHandler
*/
// 指定要拦截的位置
@ControllerAdvice(annotations = {RestController.class, Controller.class})
// 设置以json的形式返回数据
@ResponseBody
// 日志记录
@Slf4j
public class GlobalExceptionHandler {
/**
* 帐号存在异常
* @param ex
* @return
*/
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
// 获取异常信息
String exMessage = ex.getMessage();
log.error("捕获到异常:{}",exMessage);
// 处理异常信息
if(exMessage.contains("Duplicate entry")){
String[] msg = exMessage.split(" ");
return R.error("帐号 "+msg[2]+" 已存在!");
}
return R.error("未知错误!");
}
}
3. 注意事项
若项目结构为 ssm 结构,那么记得将异常处理类注入到spring容器中去!
<!--注入全局异常处理类-->
<context:component-scan base-package="top.xcyxiaoxiang.ExceptionHandler" />
参考:
Spring Boot 全局异常处理整理!开发必会! - 知乎 (zhihu.com)
springBoot 全局异常处理 - 知乎 (zhihu.com)
SpringMVC 异常处理 SimpleMappingExceptionResolver 类 博客园 (cnblogs.com)