在现代Web开发中,异常处理是一个不可或缺的部分。良好的异常处理不仅能提高系统的健壮性,还能提升用户体验。在Spring Boot中,全局异常处理的实现可以通过使用@RestControllerAdvice
注解来完成。本文将详细介绍如何使用@RestControllerAdvice
和@ExceptionHandler
来统一处理异常,并给出具体的实现示例。
@RestControllerAdvice
概述@RestControllerAdvice
是Spring MVC提供的一个功能强大的注解,用于全局处理控制器中的异常。它相当于@ControllerAdvice
和@ResponseBody
的组合,可以用于定义以下三种功能:
@ExceptionHandler
:处理特定的异常,并将响应返回给前端。@InitBinder
:预处理Web请求数据的绑定。@ModelAttribute
:将数据绑定到模型中,以便在控制器的@RequestMapping
方法中使用。@RestControllerAdvice
自动被Spring的组件扫描机制检测到,若应用通过MVC命令空间或MVC Java编程方式配置,该功能默认自动开启。
在实现全局异常处理时,我们通常会定义一个异常处理类,这个类中包含多个异常处理方法,每个方法对应一种或多种异常类型。下面是一个典型的实现示例。
import com.sugon.cloud.lowcode.result.ReturnResult; import lombok.extern.slf4j.Slf4j; import org.mybatis.spring.MyBatisSystemException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.naming.AuthenticationException; import javax.servlet.http.HttpServletRequest; import static com.sugon.cloud.lowcode.constants.SplitCharacter.LEFT_PARENTHESES; import static com.sugon.cloud.lowcode.constants.SplitCharacter.RIGHT_PARENTHESES; import static com.sugon.cloud.lowcode.result.CodeMessageEnum.*; @RestControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(value = BizException.class) @ResponseBody public ReturnResult bizExceptionHandler(HttpServletRequest req, BizException e) { log.error("发生业务异常: {}, 请求接口: {}", e.getMessage(), req.getRequestURI()); return ReturnResult.error(e.getCode(), e.getMessage()); } @ExceptionHandler(value = NullPointerException.class) @ResponseBody public ReturnResult exceptionHandler(HttpServletRequest req, NullPointerException e) { log.error("空指针异常信息: {}, 请求接口: {}", e, req.getRequestURI()); return ReturnResult.error( NULL_POINT_ERROR_EXCEPTION.getCode(), NULL_POINT_ERROR_EXCEPTION.getMessage() + RIGHT_PARENTHESES + e.getMessage() + LEFT_PARENTHESES); } @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class) @ResponseBody public ReturnResult methodNotSupportedExceptionHandler(HttpServletRequest req, Exception e) { log.error("请求方法异常信息: {},请求接口: {}", e, req.getRequestURI()); return ReturnResult.error( REQUEST_METHOD_ERROR.getCode(), REQUEST_METHOD_ERROR.getMessage() + RIGHT_PARENTHESES + e.getMessage() + LEFT_PARENTHESES); } @ExceptionHandler(value = MyBatisSystemException.class) @ResponseBody public ReturnResult sqlSyntaxErrorExceptionHandler(HttpServletRequest req, Exception e) { log.error("MyBatis系统异常信息: {},请求接口: {}", e, req.getRequestURI()); return ReturnResult.error( INNER_FRAME_EXCEPTION.getCode(), INNER_FRAME_EXCEPTION.getMessage() + RIGHT_PARENTHESES + e.getMessage() + LEFT_PARENTHESES); } @ExceptionHandler(value = AuthenticationException.class) public ReturnResult incorrectCredentialsException( HttpServletRequest request, AuthenticationException e) { log.error("用户名或密码不正确: {}, 请求接口: {}", e, request.getRequestURI()); return ReturnResult.error(USER_CREDENTIALS_ERROR); } @ExceptionHandler(value = UserException.class) public ReturnResult incorrectUserException(HttpServletRequest request, UserException e) { log.error("用户信息异常: {}, 请求接口: {}", e, request.getRequestURI()); return ReturnResult.error(e.getCode(), e.getMessage()); } @ExceptionHandler(value = Exception.class) @ResponseBody public ReturnResult exceptionHandler(HttpServletRequest req, Exception e) { e.printStackTrace(); log.error("未知异常: {}, 请求接口: {}", e, req.getRequestURI()); return ReturnResult.error( INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMessage() + RIGHT_PARENTHESES + e.getMessage() + LEFT_PARENTHESES); } }
import com.sugon.cloud.lowcode.result.BaseResultInterface; import com.sugon.cloud.lowcode.result.CodeMessageEnum; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; @ApiModel(description= "业务异常数据") public class BizException extends RuntimeException implements BaseResultInterface { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "错误码") private String code; @ApiModelProperty(value = "错误信息") private String message; public BizException() { super(); } public BizException(CodeMessageEnum codeMessageEnum) { super(codeMessageEnum.getCode()); this.code = codeMessageEnum.getCode(); this.message = codeMessageEnum.getMessage(); } public BizException(CodeMessageEnum codeMessageEnum, Throwable cause) { super(codeMessageEnum.getCode(), cause); this.code = codeMessageEnum.getCode(); this.message = codeMessageEnum.getMessage(); } public BizException(CodeMessageEnum codeMessageEnum, String message, Throwable cause) { super(codeMessageEnum.getCode(), cause); this.code = codeMessageEnum.getCode(); this.message = message; } public BizException(String message) { super(message); this.message = message; } public BizException(String code, String message) { super(code); this.code = code; this.message = message; } public BizException(String code, String message, Throwable cause) { super(code, cause); this.code = code; this.message = message; } @Override public Throwable fillInStackTrace() { return this; } @Override public String getCode() { return this.code; } @Override public String getMessage() { return this.message; } }
在上述代码中,所有的异常处理方法返回的是ReturnResult
对象,它封装了返回给前端的数据。ReturnResult
类的设计使得前端可以统一解析和展示错误信息。
import com.alibaba.fastjson.JSONObject; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Getter; import lombok.Setter; import static com.sugon.cloud.lowcode.result.CodeMessageEnum.*; @Setter @Getter @ApiModel(description= "返回响应数据") public class ReturnResult { @ApiModelProperty(value = "状态码") private String code; @ApiModelProperty(value = "响应信息") private String message; @ApiModelProperty(value = "响应对象") private T result; @ApiModelProperty(value = "是否成功") private boolean success = true; public ReturnResult() {} public ReturnResult(CodeMessageEnum codeMessageEnum) { this.code = codeMessageEnum.getCode(); this.message = codeMessageEnum.getMessage(); } public static ReturnResult success() { return success(null); } public static ReturnResult success(T data) { return success(SUCCESS, data); } public static ReturnResult loginSuccess(T data) { return success(SUCCESS_LOGIN, data); } public static ReturnResult loginOutSuccess() { return success(SUCCESS_LOGOUT, null); } private static ReturnResult success(CodeMessageEnum codeMessageEnum, T data) { ReturnResult result = new ReturnResult<>(); result.setCode(codeMessageEnum.getCode()); result.setMessage(codeMessageEnum.getMessage()); result.setResult(data); return result; } public static ReturnResult error(CodeMessageEnum codeMessageEnum) { return error(codeMessageEnum.getCode(), codeMessageEnum.getMessage()); } public static ReturnResult error(CodeMessageEnum codeMessageEnum, String message) { return error(codeMessageEnum.getCode(), message); } public static ReturnResult error(String code, String message) { ReturnResult result = new ReturnResult(); result.setCode(code); result.setMessage(message); result.setSuccess(false); return result; } @Override public String toString() { return JSONObject.toJSONString(this); } }
通过@RestControllerAdvice
和@ExceptionHandler
,我们可以在Spring Boot应用中实现全局异常处理,统一管理和处理所有异常。这样不仅可以
减少重复代码,还能使代码更加整洁,提高可维护性。同时,通过统一的返回格式,前端处理响应数据时也能更加方便。
希望这篇文章对您在Spring Boot中实现全局异常处理有所帮助。如有任何问题或建议,欢迎交流探讨。