Spring boot 优雅实现全局自定义异常
一、前言:
SpringBoot的项目已经对有一定的异常处理了,但是对于我们开发者而言可能就不太合适了,因此我们需要对这些异常进行统一的捕获并处理。SpringBoot中有一个ControllerAdvice
的注解,使用该注解表示开启了全局异常的捕获,我们只需在自定义一个方法使用ExceptionHandler
注解然后定义捕获异常的类型即可对这些捕获的异常进行统一的处理。
二、目的
解决错误异常清晰、异常扩展性强、统一管理异常枚举类。
看了网上一些自定义异常,觉得不是太适用项目,异常展示不是太清晰,自己写了一套全局异常,只是为了解决开发者易于观察,方便扩展,如有不足之处还望大佬赐教。
三、目录截图
四、开始编码
1.配置application.yml,方便开发者自定义开发模式,用于项目获取模式,展示异常。
1 server: 2 port: 80 3 #spring相关配置 4 spring: 5 profiles: 6 active: @spring.active@
2.异常枚举基类,多个实现,用于统一管理异常枚举类。
1 /** 2 * 异常基类 3 */ 4 public interface BaseExceptionEnum { 5 6 public String getMessage(); 7 8 }
3.鉴权模块异常枚举类、只定义了一个异常类,如果有其他模块、可以定义多个实现BaseExceptionEnum ,用于异常管理,好找。
1 /** 2 * 鉴权模块异常----按照模块划分 3 */ 4 public enum AuthExceptionEnum implements BaseExceptionEnum { 5 /** 6 * 账号或密码为空 7 */ 8 USERNAME_PASS_NULL("账号或密码为空"), 9 /** 10 * 登录验证码为空或过期 11 */ 12 USER_VERIFICATION_CODE_NULL("登录验证码为空或已过期"), 13 /** 14 * 登录验证码错误 15 */ 16 USER_VERIFICATION_CODE("登录验证码错误"), 17 /** 18 * 登录密码错误 19 */ 20 USERNAME_PASS_ERROR("登录密码错误"), 21 /** 22 * 登录失败 23 */ 24 USER_LOGIN__ERROR("登录失败"), 25 /** 26 * 登录用户不存在 27 */ 28 USER_NON_EXISTENT("登录用户不存在"), 29 /** 30 * 登录用户不存在 31 */ 32 USER_HAS_BEEN_DELETE("登录用户不存在!"), 33 /** 34 * 登录用户已冻结 35 */ 36 USER_FROZEN( "登录用户已冻结!"); 37 38 39 40 private final String message; 41 42 AuthExceptionEnum(String message) { 43 this.message = message; 44 } 45 AuthExceptionEnum(BaseExceptionEnum baseExceptionEnum) { 46 this.message =baseExceptionEnum.getMessage(); 47 } 48 49 @Override 50 public String getMessage() { 51 return message; 52 } 53 }
4.定义统一异常类
1 /** 2 * 异常统一 3 */ 4 public class BaseException extends RuntimeException { 5 6 private static final long serialVersionUID = 1L; 7 8 private final int code; 9 private final String value; 10 11 protected BaseException(int code, String message) { 12 super(message); 13 this.code = code; 14 this.value = message; 15 } 16 public int getCode() { 17 return this.code; 18 } 19 }
5.自定义响应用户异常类
1 /** 2 * 用户请求错误 3 * 向用户展示的异常错误对象 400 {"code":"1","message":"exception.message"} 4 */ 5 public class UserAlertException extends BaseException { 6 7 public UserAlertException(String message) { 8 super(1, message); 9 } 10 public UserAlertException(int code, String message) { 11 super(code, message); 12 } 13 public UserAlertException(BaseExceptionEnum exceptionEnum) { 14 super(1, exceptionEnum.getMessage()); 15 } 16 }
4.编写开发模式工厂类获取,方便扩展、易于维护。
1 /** 2 * @ClassName: ExceptionPattern 3 * @Description: 异常模式 4 * @Author: fuzongle 5 * @create: 2020-11-24 13:43 6 **/ 7 public class ExceptionPatternFactory { 8 9 public Map<String, Function<Exception, ResponseResult>> map = new HashMap<>(); 10 11 { 12 map.put("dev", this::dev); 13 map.put("prod", this::prod); 14 } 15 16 //编写生产环境的业务错误异常逻辑 17 public ResponseResult prod(Exception e) { 18 return ResponseResult.builder().build(); 19 } 20 21 //编写开发环境的业务错误异常逻辑 22 public ResponseResult dev(Exception e) { 23 return new ExceptionFactory().map.get(e.getClass().getName()).apply(e); 24 } 25 26 27 }
5.编写异常统一返回格式、打印日志工厂类、本身想把这个用在开发模式、线上模式在建一个类维护线上的异常、现在项目上线大部分异常看日志的话、阿里云不是太方便、定位问题太慢,想集成存储异常日志,提供ui展示、也是再次基础上扩展生成模式即可。
1 /** 2 * @ClassName: ExceptionPattern 3 * @Description: 异常模式 4 * @Author: fuzongle 5 * @create: 2020-11-24 13:43 6 **/ 7 @Log4j2 8 public class ExceptionFactory { 9 10 public Map<Object, Function<Object, ResponseResult>> map = new HashMap<>(); 11 12 { 13 map.put(UserAlertException.class.getName(), this::UserAlertException); 14 map.put(ArithmeticException.class.getName(), this::ArithmeticException); 15 } 16 17 18 /** 19 * 用户请求错误 像用户展示 20 */ 21 public ResponseResult UserAlertException(Object e) { 22 UserAlertException userAlertException = (UserAlertException) e; 23 24 PrintlnErrorLog.printlnErrorLog("->>>自定义异常-给用户展示错误对象", 25 ErrorUtils.getStackTrace(userAlertException), 26 userAlertException.getStackTrace()[0]); 27 28 return ResponseResult.builder().code(userAlertException.getCode()).message(userAlertException.getMessage()).build(); 29 } 30 31 /** 32 * 算术运算异常 33 */ 34 public ResponseResult ArithmeticException(Object e) { 35 ArithmeticException userAlertException = (ArithmeticException) e; 36 37 PrintlnErrorLog.printlnErrorLog("->>>算术运算异常", 38 ErrorUtils.getStackTrace(userAlertException), 39 userAlertException.getStackTrace()[0]); 40 41 return ResponseResult.builder().code(HttpStatus.BAD_REQUEST.value()).message(userAlertException.getMessage()).build(); 42 } 43 44 45 46 }
5.打印异常日志、方法、可以提供开发者快速定位问题。
1 /** 2 * 异常日志 3 */ 4 public static void printlnErrorLog(String title,String errInfo,StackTraceElement stackTraceElement) { 5 6 log.info("\n-----------------------异常分析 start-----------------------------------\n\t" + 7 "异常标题:{}\n\t" + 8 "文件名:{}\n\t" + 9 "类名:{}\n\t" + 10 "方法名:{}\n\t" + 11 "抛出异常行号:{}\n\t" + 12 "异常栈信息:{}\n\t" + 13 "解决方案: {}\n" + 14 "------------------异常分析 end ---------------------------------------", 15 title, 16 stackTraceElement.getFileName(), 17 stackTraceElement.getClassName(), 18 stackTraceElement.getMethodName(), 19 stackTraceElement.getLineNumber(), 20 errInfo, 21 "暂无"); 22 }
6.实现全局控制器通知、全局捕获异常、统一返回方式。@ExceptionHandler 拦截了异常,我们可以通过该注解实现自定义异常处理。其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型,上面拦截了 Exception.class 这种异常。
1 /** 2 * @ClassName: ExceptionPattern 3 * @Description: 全局异常配置 4 * @Author: fuzongle 5 * @create: 2020-11-25 12:43 6 **/ 7 @Slf4j 8 @RestControllerAdvice 9 public class GlobalExceptionHandler { 10 11 12 /** 13 * 获取开发模式、生产模式、默认dev 14 */ 15 @Value("${spring.profiles.active}") 16 private String var; 17 18 /** 19 * 全局异常. 20 */ 21 @ExceptionHandler(Exception.class) 22 public ResponseEntity<ResponseResult> handleGlobalException(Exception e) { 23 ResponseResult apply = new ExceptionPatternFactory().map.get(var).apply(e); 24 int httpStatus = Objects.equals(HttpStatus.INTERNAL_SERVER_ERROR.value(), apply.getCode()) ? HttpStatus.INTERNAL_SERVER_ERROR.value() : HttpStatus.BAD_REQUEST.value(); 25 return new ResponseEntity<>(apply, HttpStatus.valueOf(httpStatus)); 26 } 27 28 29 }
五、测试异常
1.直接编写API浏览器请求看结果。
1 /** 2 * @Description: 测试 3 **/ 4 @RestController 5 public class ErrorController { 6 7 /** 8 * 自定义异常测试 9 * @return 10 */ 11 @RequestMapping("/test") 12 public String test() { 13 throw new UserAlertException(AuthExceptionEnum.USER_FROZEN); 14 } 15 16 /** 17 * 系统捕获的异常 18 */ 19 @RequestMapping("/ArithmeticException") 20 public void ArithmeticException() { 21 System.out.println(1/0); 22 } 23 }
2.响应结果,结果非常清晰,可以在开发的地方遇见异常记录异常维护、ExceptionFactory维护异常,百度找到的解决方案可以再次记录,方便下次不在忘记错误,提高了开发效率。
六、温馨提示
1.如果有任何不懂的地方可以咨询我,随时欢迎互相帮助,如果写的不好的地方请大佬指教。
2.技术交流群QQ:422167709。
3.如果希望学习更多,希望微信扫码,长按扫码,帮忙关注一下,举手之劳,当您无助的时候真的能帮你。
4.获取源代码:扫码关注。回复任意字符、加作者微信秒发源码。