此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。
  本文为Spring Boot使用@ControllerAdvice进行自定义异常捕捉及处理
本系列文章中所使用的框架版本为Spring Boot 2.0.3-RELEASE,Spring 5.0.7-RELEASE,Dubbo 2.6.2。
注解
@ControllerAdvice
控制器增强,所有在Controller中被抛出的异常将会被使用@ControllerAdvice注解的类捕捉。默认情况下,	@ControllerAdvice全局应用于所有控制器的方法。
若所有异常处理类返回的数据格式为
json,则可以使用@RestControllerAdvice代替@ControllerAdvice@RestControllerAdvice = @ControllerAdvice + @ResponseBody
@ExceptionHandler
异常处理器,在特定异常处理程序的类或方法中以处理异常的注解。
业务异常
常规的Controller层,会抛出业务异常、校检异常等
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
@RequestMapping(value = "/")
public class TestController {
    @Autowired
    private BussinessService bussinessService ;
    @RequestMapping(value = "/basic", method = RequestMethod.POST)
    public Resp<RespVO> getBasic(@RequestBody @Valid ReqVO reqVO) {
		RespVO respVO = bussinessService.getResult(reqVO);
		if(respVO  == null)
			throws BussinessExceptionMapper.build("调用失败");
        return Resp.createSuccess(respVO);
    }
}
业务异常定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
@Setter
@Getter
public class BussinessException extends RuntimeException {
    public static final String UNKNOWN_EXCEPTION = "unknown.exception";
    public static final String SERVICE_FAIL = "service.fail";
    public static final String NETWORK_EXCEPTION = "network.exception";
    public static final String TIMEOUT_EXCEPTION = "timeout.exception";
    private String code;
    public BussinessException() {
        super();
    }
    public BussinessException(String message, Throwable cause) {
        super(message, cause);
    }
    public BussinessException(String message) {
        super(message);
    }
    public BussinessException(Throwable cause) {
        super(cause);
    }
    public BussinessException(String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }
    public BussinessException(String code, String message) {
        super(message);
        this.code = code;
    }
}
业务异常构造类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class BussinessExceptionMapper {
    public static BussinessException build(String message) {
        return build(BussinessException.SERVICE_FAIL, message);
    }
    public static BussinessException build(String code, String message) {
        return new BussinessException(code, message);
    }
    public static BussinessException build(String message, Throwable cause) {
        return build(BussinessException.SERVICE_FAIL, message, cause);
    }
    public static BussinessException build(String code, String message, Throwable cause) {
        return new BussinessException(code, message, cause);
    }
}
异常处理
@Controller 及 @RestController中的异常捕捉及处理
参数校检异常
使用hibernate validator校检参数,@Valid注解抛出的校检异常为MethodArgumentNotValidException,在@ExceptionHandler注释的方法中捕捉第一个错误信息
1
2
3
4
5
6
7
8
9
10
11
12
13
@ResponseBody
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Resp validHandler(MethodArgumentNotValidException ex) {
    logger.error("ValidException:", ex);
    List<ObjectError> list = ex
            .getBindingResult().getAllErrors();
    String errorMsg = StringUtils.defaultString(list.get(0).getDefaultMessage(), "参数错误");
    return Resp.createError(RespCode.PARAM_ERR, "input.param.error", errorMsg);
}
业务异常
1
2
3
4
5
6
7
8
@ResponseBody
@ExceptionHandler(value = BussinessException.class)
public Resp bussinessHandler(BussinessException ex) {
    logger.error("BussinessException:", ex);
    return Resp.createError(RespCode.BUSINESS_INVALID, ex.getCode(), ex.getMessage());
}
Dubbo Rpc异常及运行时异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
@ControllerAdvice
public class GlobalControllerAdvice {
    private Logger logger = LoggerFactory.getLogger(GlobalControllerAdvice.class);
    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Resp validHandler(MethodArgumentNotValidException ex) {
        logger.error("ValidException:", ex);
        List<ObjectError> list = ex
                .getBindingResult().getAllErrors();
        String errorMsg = StringUtils.defaultString(list.get(0).getDefaultMessage(), "参数错误");
        return Resp.createError(RespCode.PARAM_ERR, "input.param.error", errorMsg);
    }
    @ResponseBody
    @ExceptionHandler(value = BussinessException.class)
    public Resp bussinessHandler(BussinessException ex) {
        logger.error("BussinessException:", ex);
        return Resp.createError(RespCode.BUSINESS_INVALID, ex.getCode(), ex.getMessage());
    }
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Resp errorHandler(Exception e) {
        logger.error("uncaught Exception:", e);
        Resp resp;
        if (e instanceof RpcException) {
            resp = Resp.createError(RespCode.ERROR, "rpc.exception",
                    e.getMessage());
        } else if (e instanceof RuntimeException) {
            resp = Resp.createError(RespCode.ERROR, "runtime.exception",
                    "运行时异常,详见堆栈日志");
        } else {
            resp = Resp.createError(RespCode.BUSINESS_INVALID,
                    "service.fail", "服务失败");
        }
        return resp;
    }
}
参考资料:  
1.ControllerAdvice  
2.RestControllerAdvice
