前言

简单回顾下异常分类:

  1. 非受检异常:运行时异常
  2. 受检异常:编译时异常

异常的处理通常有两种方式:

  1. 第一种就是在当前类使用try-catch捕获异常并直接处理
  2. 另一种就是不在当前类处理,使用throws抛给上一级处理

如果在每个异常处都进行try-catch处理的话,无疑会造成代码的臃肿,且不够美观。
所以,一般来说除非的必须在在当前类处理的异常,否则都会向上抛出,直到抛到controller层为止,在controller层进行统一的异常处理(不处理的话,再往上抛就抛到前端去了)。

统一进行异常处理的常见方式有两种:1. 使用注解 或者 2. 使用(AOP)拦截器

统一返回结果

既然要统一进行异常处理了,自然需要统一下数据结果的返回格式,这里建议使用一个实体类进行处理,更加的可控易于管理修改。
在实体类中对数据的返回格式进行下规范:

  1. import io.swagger.annotations.ApiModelProperty;
  2. import lombok.Data;
  3. import java.util.Collection;
  4. import java.util.HashMap;
  5. @Data
  6. public class ReturnResult {
  7. @ApiModelProperty(value = "操作结果")
  8. private Boolean result;
  9. @ApiModelProperty(value = "返回消息")
  10. private String message;
  11. @ApiModelProperty(value = "返回数据")
  12. private HashMap<String, String> data;
  13. private ReturnResult() {
  14. }
  15. public static ReturnResult error() {
  16. ReturnResult returnResult = new ReturnResult();
  17. returnResult.setResult(false);
  18. returnResult.setMessage("操作出错");
  19. return returnResult;
  20. }
  21. public static ReturnResult error(HashMap<String, String> data) {
  22. ReturnResult returnResult = new ReturnResult();
  23. returnResult.setResult(false);
  24. returnResult.setMessage("操作出错");
  25. returnResult.setData(data);
  26. return returnResult;
  27. }
  28. public static ReturnResult success() {
  29. ReturnResult returnResult = new ReturnResult();
  30. returnResult.setResult(true);
  31. returnResult.setMessage("操作成功");
  32. return returnResult;
  33. }
  34. public static ReturnResult success(HashMap<String, String> data) {
  35. ReturnResult returnResult = new ReturnResult();
  36. returnResult.setResult(true);
  37. returnResult.setMessage("操作成功");
  38. returnResult.setData(data);
  39. return returnResult;
  40. }
  41. public ReturnResult result(Boolean success) {
  42. this.setResult(success);
  43. return this;
  44. }
  45. public ReturnResult message(String message) {
  46. this.setMessage(message);
  47. return this;
  48. }
  49. }

统一异常处理

controller类示例:

  1. @RestController
  2. public class ErrorTestController {
  3. @Autowired
  4. ErrorService errorService;
  5. @GetMapping("/test")
  6. public ReturnResult toTest(){
  7. HashMap<String, String> map = new HashMap<>();
  8. map.put("data", "这里是返回的数据");
  9. return ReturnResult.success(map);
  10. }
  11. @GetMapping("/test1")
  12. public ReturnResult toTest1(){
  13. errorService.error1();
  14. return ReturnResult.success();
  15. }
  16. @GetMapping("/test2")
  17. public ReturnResult toTest2(){
  18. errorService.error2();
  19. return ReturnResult.success();
  20. }
  21. }

同时在service类中模拟异常:

  1. @Service
  2. public class ErrorService {
  3. public void error1() throws NullPointerException {
  4. String[] str = null;
  5. String s = str[0];
  6. }
  7. public void error2() throws ArithmeticException {
  8. int i = 9 / 0;
  9. }
  10. }

AOP处理异常

使用AOP切controller类,进行统一的异常处理。

AOP切面类:

  1. import email.entity.ReturnResult;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Around;
  4. import org.aspectj.lang.annotation.Aspect;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import org.springframework.stereotype.Component;
  8. import java.util.HashMap;
  9. @Aspect
  10. @Component
  11. public class AOPCatch {
  12. private final Logger logger = LoggerFactory.getLogger(AOPCatch.class);
  13. @Around("execution(public * email.controller.*.*(..))")
  14. public ReturnResult catchException(ProceedingJoinPoint joinPoint) {
  15. try {
  16. Object result = joinPoint.proceed();
  17. // 没有异常,直接将接收到的被切方法的返回值原样返回
  18. return (ReturnResult) result;
  19. } catch (Throwable e) {
  20. HashMap<String, String> map = new HashMap<>();
  21. String className = joinPoint.getTarget().getClass().getName();
  22. String methodName = joinPoint.getSignature().getName();
  23. logger.error(className + ":" + methodName + ":" + e);
  24. map.put("data", className + ":" + methodName + ":" + e);
  25. ReturnResult error = ReturnResult.error(map);
  26. // 有异常,则返回错误信息
  27. return error;
  28. }
  29. }
  30. }

s10101.pngs10102.png

注解处理异常

使用@ControllerAdvice或者@RestControllerAdvice配合@ExceptionHandler注解使用

@RestControllerAdvice注解就相当于@ControllerAdvice+@ResponseBody注解,标识该类中接收的返回信息的(json类型的)数据。

创建一个统一的异常处理类:

  1. package email.servive;
  2. import email.entity.ReturnResult;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import org.springframework.web.bind.annotation.ExceptionHandler;
  6. import org.springframework.web.bind.annotation.RestControllerAdvice;
  7. import java.util.HashMap;
  8. @RestControllerAdvice
  9. //@ControllerAdvice
  10. //@ResponseBody
  11. public class GlobalExceptionHandler {
  12. private final Logger logger = LoggerFactory.getLogger(AOPCatch.class);
  13. // 全局异常处理
  14. @ExceptionHandler(Exception.class)
  15. public ReturnResult error(Exception e){
  16. e.printStackTrace();
  17. logger.error(e.getMessage());
  18. return ReturnResult.error();
  19. }
  20. // 指定异常处理
  21. @ExceptionHandler(NullPointerException.class)
  22. public ReturnResult error(NullPointerException e){
  23. e.printStackTrace();
  24. logger.error(e.getMessage());
  25. HashMap<String, String> map = new HashMap<>();
  26. map.put("data", "空指针异常");
  27. return ReturnResult.error(map);
  28. }
  29. // 自定义异常处理,针对于业务需求自定义的
  30. /*
  31. @ExceptionHandler(MyException.class)
  32. public ReturnResult error(MyException e){
  33. e.printStackTrace();
  34. return ReturnResult.error();
  35. }
  36. */
  37. }

s10104.pngs10105.pngs10103.png