一、设计一个优秀的异常处理机制
在Spring Boot中设计一个优秀的异常处理机制,可以确保应用程序在遇到错误时提供清晰、一致的响应,同时提高系统的健壮性和可维护性。
以下是一个关于如何设计Spring Boot异常处理机制的步骤和建议:
1)定义自定义异常类
- 创建自定义异常类来代表应用程序中可能发生的特定错误情况。
- 自定义异常类应该扩展标准的
RuntimeException
或Exception
类,并提供一个明确的消息来描述错误。
2)创建全局异常处理器
- 使用
@ControllerAdvice
注解创建一个全局异常处理器类。 - 在该类中,使用
@ExceptionHandler
注解来指定处理特定异常的方法。
3)定义统一的异常响应格式
- 定义一个统一的异常响应格式,比如一个包含状态码、错误消息和可能还有其他相关信息的JSON对象。
- 在全局异常处理器中,确保所有捕获的异常都被转换成这种统一的格式。
4)异常分层
- 根据错误的严重程度和影响范围,对异常进行分层。
- 可以定义不同的异常类来表示不同的错误级别,例如:业务逻辑错误、系统错误、安全错误等。
5)日志记录
- 在异常处理过程中,确保记录适当的日志信息。
- 使用Spring Boot的日志框架(如Logback或Log4j)来记录异常的堆栈跟踪和其他重要信息。
6)测试
- 编写单元测试来验证异常处理机制的行为。
- 确保在出现预期异常时,处理程序能够正确捕获并记录异常,同时返回正确的响应格式。
7)提供友好的用户错误信息
- 不要在前端界面上直接显示原始异常消息或堆栈跟踪。
- 提供给用户友好、易于理解的错误信息和建议。
8)处理全局异常
- 使用
@ExceptionHandler(Exception.class)
来处理所有未被其他处理器捕获的异常。 - 这可以作为一个“兜底”处理器,确保所有异常都被处理。
9)考虑国际化
- 如果你的应用程序需要支持多种语言,确保异常消息是可以国际化的。
- 使用Spring的消息源(MessageSource)来支持多语言错误消息。
10)优雅地处理资源不足
- 当应用程序遇到资源不足(如数据库连接池耗尽)等问题时,应确保异常得到妥善处理,避免应用崩溃。
- 考虑实现资源耗尽时的回退策略,如优雅地降级服务。
11)使用AspectJ进行切面编程
- 可以使用AspectJ的切面编程来在方法执行前后进行异常处理,从而避免在每个控制器中重复编写异常处理逻辑。
通过遵循这些步骤,你可以设计一个强大而灵活的异常处理机制,提高Spring Boot应用程序的健壮性和用户体验。
二、统一异常处理实现步骤
1、统一数据响应
我们必须为所有的接口定义统一的数据响应格式,创建统一数据响应类
@DatapublicclassAjaxResponse<T>{privateString message;privateInteger code;privateT data;publicstaticAjaxResponse<?>error(CustomException e){AjaxResponse<?> response =newAjaxResponse<>();
response.setCode(e.getCode());
response.setMessage(e.getMsg());return response;}publicstaticAjaxResponse<?>success(){AjaxResponse<?> response =newAjaxResponse<>();
response.setCode(ErrorCode.SUCCESS.getCode());
response.setMessage(ErrorCode.SUCCESS.getMsg());return response;}publicstatic<T>AjaxResponse<T>success(T data){AjaxResponse<T> response =newAjaxResponse<>();
response.setCode(ErrorCode.SUCCESS.getCode());
response.setMessage(ErrorCode.SUCCESS.getMsg());
response.setData(data);return response;}}
2、定义统一的异常状态码
建议直接使用Http状态码
@GetterpublicenumErrorCode{SUCCESS(200,"成功"),BAD_REQUEST(400,"请求参数不正确"),SERVER_ERROR(500,"系统异常"),UNKNOWN(999,"未知错误");privateInteger code;privateString msg;ErrorCode(Integer code,String message){this.code = code;this.msg = message;}}
3、创建自定义异常类
创建自定义异常CustomException,使用统一的异常枚举类ErrorCode作为参数
@GetterpublicclassCustomExceptionextendsRuntimeException{privateInteger code;privateString msg;publicCustomException(){super();}publicCustomException(ErrorCode errorCode){this.code = errorCode.getCode();this.msg = errorCode.getMsg();}publicCustomException(ErrorCode errorCode,String msg){this.code = errorCode.getCode();this.msg = msg;}}
4、创建全局异常处理类
创建全局异常处理类
WebExceptionHandler
,并使用注解
@ControllerAdvice
声明。
拦截异常,并封装成统一的数据返回格式
AjaxResponse
@ControllerAdvicepublicclassWebExceptionHandler{@ExceptionHandler(CustomException.class)@ResponseBodypublicAjaxResponse<?>customerException(CustomException e){returnAjaxResponse.error(e);}@ExceptionHandler(Exception.class)@ResponseBodypublicAjaxResponse<?>exception(Exception e){returnAjaxResponse.error(newCustomException(ErrorCode.UNKNOWN));}}
5、创建通用响应处理类
创建通用返回处理类
GlobalResponseHandler
,拦截所有的接口返回数据,将接口请求的HttpCode,设置为AjaxResponse的Code,保证在异常抛出时,前端能够感知到是异常请求。
@ControllerAdvicepublicclassGlobalResponseHandlerimplementsResponseBodyAdvice{@Overridepublicbooleansupports(MethodParameter returnType,Class converterType){if(returnType.getMethod()==null){returnfalse;}ResponseBody responseBody = returnType.getMethod().getAnnotation(ResponseBody.class);if(responseBody !=null){returntrue;}// 只拦截返回结果为 AjaxResponse 类型return returnType.getMethod().getReturnType()==AjaxResponse.class;}@OverridepublicObjectbeforeBodyWrite(Object body,MethodParameter returnType,MediaType selectedContentType,Class selectedConverterType,ServerHttpRequest request,ServerHttpResponse response){ResponseBody responseBody = returnType.getMethod().getAnnotation(ResponseBody.class);if(responseBody !=null|| selectedContentType.equalsTypeAndSubtype(MediaType.APPLICATION_JSON)){if(body instanceofAjaxResponse<?> ajaxBody){if(!Objects.equals(ajaxBody.getCode(),ErrorCode.UNKNOWN.getCode())){
response.setStatusCode(HttpStatus.valueOf(ajaxBody.getCode()));}}else{returnAjaxResponse.success(body);}}return body;}}
6、创建测试类,模拟抛出异常
1)创建测试异常Service
@ServicepublicclassExceptionService{publicvoidserverError(){try{Class.forName("com.mysql.jdbc.xxx.Driver");}catch(Exception e){thrownewCustomException(ErrorCode.SERVER_ERROR,"数据库驱动加载异常,出现ClassNotFoundException,请联系管理员");}}publicvoidbadRequest(){thrownewCustomException(ErrorCode.BAD_REQUEST,"您输入的数据不符合业务逻辑,请确认后重新输入!");}}
2)创建测试Controller方法
@GetMapping("/user")@ResponseBodypublicUseruser(User user){Assert.isTrue(user.getName().equals("jackson"),"User must be a jackson");if(user.getAge()<18& user.getAge()>=0){
exceptionService.badRequest();}elseif(user.getAge()<0){
exceptionService.serverError();}return user;}
可以使用postman进行测试,传入不同的参数抛出不同异常
三、 @ControllerAdvice 特别说明
@ControllerAdvice是Spring 3.2及以后版本中引入的一个注解,它用于全局地处理控制器层的异常和其他跨切面的关注点。该注解提供了一种集中的方式,使得开发者可以在单个位置定义并管理多个控制器中可能遇到的通用逻辑。
具体来说,@ControllerAdvice的作用主要体现在以下几个方面:
- 全局异常处理:通过结合
@ExceptionHandler
注解,开发者可以定义全局级别的异常处理逻辑。这避免了在每个控制器中重复编写相同的异常处理代码。当控制器中抛出异常时,Spring MVC会查找带有@ControllerAdvice
注解的类,并尝试调用匹配的@ExceptionHandler
方法来处理异常。 - 数据绑定:通过结合
@InitBinder
注解,开发者可以在多个控制器之间配置通用的WebDataBinder设置。这对于自定义请求参数的绑定和格式化非常有用。例如,开发者可以定义全局的日期格式、数字格式等。 - 模型增强:通过结合
@ModelAttribute
注解,开发者可以在多个控制器间添加公共的模型属性。这对于添加那些需要在多个控制器或视图中使用的数据非常方便。例如,开发者可以在一个带有@ControllerAdvice注解的类中定义一个方法,该方法会在每个控制器方法执行之前执行,从而向模型中添加一些公共属性。
总的来说,
@ControllerAdvice
注解提供了一个强大的机制,使得开发者能够以一种集中和模块化的方式处理控制器层的异常和其他跨切面的关注点。这不仅提高了代码的可维护性和可重用性,还使得异常处理和数据绑定等逻辑更加清晰和易于管理。
四、Http状态码
状态码类别原因短语描述100信息性响应Continue请求已收到,请继续发送101信息性响应Switching Protocols切换协议102信息性响应Processing请求正在处理中200成功OK请求成功201成功Created请求成功且资源已创建202成功Accepted请求已接受,处理中203成功Non-Authoritative Information非授权信息204成功No Content无内容205成功Reset Content重置内容206成功Partial Content部分内容300重定向Multiple Choices多种选择301重定向Moved Permanently永久移动302重定向Found临时移动303重定向See Other查看其他位置304重定向Not Modified未修改307重定向Temporary Redirect临时重定向400客户端错误Bad Request错误请求401客户端错误Unauthorized未授权402客户端错误Payment Required需要付款403客户端错误Forbidden禁止访问404客户端错误Not Found未找到资源405客户端错误Method Not Allowed方法不允许406客户端错误Not Acceptable不可接受407客户端错误Proxy Authentication Required需要代理身份验证408客户端错误Request Timeout请求超时409客户端错误Conflict冲突410客户端错误Gone资源已消失500服务器错误Internal Server Error服务器内部错误501服务器错误Not Implemented未实现功能502服务器错误Bad Gateway错误的网关503服务器错误Service Unavailable服务不可用504服务器错误Gateway Timeout网关超时505服务器错误HTTP Version Not Supported不支持的HTTP版本506服务器错误Variant Also Negotiates协商变体也存在507服务器错误Insufficient Storage存储不足508服务器错误Loop Detected检测到循环
参考
版权归原作者 顽石九变 所有, 如有侵权,请联系我们删除。