0%

SpringBoot 全局异常处理进阶:使用 @ControllerAdvice 对不同的 Controller 分别捕获异常并处理

引言

之前发表过一篇 SpringBoot 对 controller 层捕获全局异常并处理的方法(@ControllerAdvice 和 @ExceptionHandler),介绍了如何在 SpringBoot 工程中对 Controller 配置全局异常处理。后来在实际工程中,又有了如下需求:有些接口在发生异常时,需要持久化错误信息,而有的接口则不需要。如果使用了全局异常处理,那每次发生了异常,还需要判断是哪个接口发生的异常,进而选择是否持久化错误日志。那能不能对全局异常进行配置,对不同类型的接口使用不同的全局异常进行处理呢?

经过查阅文档发现,Spring 提供了对 @ControllerAdvice 注解的配置,我们可以通过配置 @ControllerAdvice 指定拦截范围。

@ControllerAdvice 指定 Controller 范围

根据 API,我们可以看到注解 @ControllerAdvice 有如下几种配置:

basePackages

1
2
3
4
//@ControllerAdvice("cn.myz.demo.controller")
//@ControllerAdvice(value = "cn.myz.demo.controller")
@ControllerAdvice(basePackages = {"cn.myz.demo.controller"})
public class GlobalExceptionHandler {}

basePackages:指定一个或多个包,这些包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。其中上面两种等价于 basePackages。

basePackageClasses

1
2
@ControllerAdvice(basePackageClasses = {MyController1.class})
public class GlobalExceptionHandler {}

basePackageClasses:是 basePackages 的一种变形,指定一个或多个 Controller 类,这些类所属的包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理。

assignableTypes

1
2
@ControllerAdvice(assignableTypes = {MyController1.class})
public class GlobalExceptionHandler {}

assignableTypes:指定一个或多个 Controller 类,这些类被该 @ControllerAdvice 管理。

annotations

1
2
@ControllerAdvice(annotations = {RestController.class})
public class GlobalExceptionHandler {}

annotations:指定一个或多个注解,被这些注解所标记的 Controller 会被该 @ControllerAdvice 管理。

例子

这里我用 assignableTypes 配置指定的 Controller 进行测试。

创建三个 Controller

1
2
3
4
5
6
7
8
@Controller
public class MyController1 {

@RequestMapping(value = "/test1")
public void test1() {
throw new BusinessException("1", "test1 错误");
}
}
1
2
3
4
5
6
7
@Controller
public class MyController2 {
@RequestMapping(value = "/test2")
public void test1() {
throw new BusinessException("2", "test2 错误");
}
}
1
2
3
4
5
6
7
@Controller
public class MyController3 {
@RequestMapping(value = "/test3")
public void test1() {
throw new BusinessException("3", "test3 错误");
}
}

其中,BusinessException 是我自定义的异常类。

创建两个全局异常处理类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@ControllerAdvice(assignableTypes = {MyController1.class})
@Slf4j
public class GlobalExceptionHandler1 {

/**
* 处理 Exception 异常
*
* @param httpServletRequest httpServletRequest
* @param e 异常
* @return
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public String exceptionHandler(HttpServletRequest httpServletRequest, Exception e) {
log.error("GlobalExceptionHandler1 服务错误");
return "GlobalExceptionHandler1 服务错误";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@ControllerAdvice(assignableTypes = {MyController2.class})
@Slf4j
public class GlobalExceptionHandler2 {

/**
* 处理 Exception 异常
*
* @param httpServletRequest httpServletRequest
* @param e 异常
* @return
*/
@ResponseBody
@ExceptionHandler(value = Exception.class)
public String exceptionHandler(HttpServletRequest httpServletRequest, Exception e) {
log.error("GlobalExceptionHandler2 服务错误");
return "GlobalExceptionHandler2 服务错误";
}
}

分别调用接口,查看错误日志

  1. 调用 localhost:8080/test1
    返回:GlobalExceptionHandler1 服务错误
    即 MyController1 异常被 GlobalExceptionHandler1 全局异常类捕获。

  2. 调用 localhost:8080/test2
    返回:GlobalExceptionHandler2 服务错误
    即 MyController2 异常被 GlobalExceptionHandler2 全局异常类捕获。

  3. 调用 localhost:8080/test3

返回:

1
2
3
4
5
6
7
{
"timestamp": "2019-03-15T06:40:06.224+0000",
"status": 500,
"error": "Internal Server Error",
"message": "No message available",
"path": "/test3"
}

即 MyController3 异常没有被全局异常捕获。

以上就是全局异常的分类处理。


站在前人的肩膀上前行,感谢以下资料的支持。

  • spring-framework-reference
  • javadoc-api-ControllerAdvice
  • spring-boot-reference
  • 本文作者: 因特马
  • 本文链接: https://www.interhorse.cn/a/799497670/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!