企业级异常处理方案:Spring Boot自定义异常全局拦截实战
1. 创建自定义异常类
public class ApproveException extends Exception {public ApproveException() {}public ApproveException(String message) {super(message);}
}public class HandleException extends Exception {public HandleException() {}public HandleException(String message) {super(message);}
}public class QueryException extends Exception {public QueryException() {}public QueryException(String message) {super(message);}
}
2. 创建全局异常处理器
package jnpf.exception;import cn.dev33.satoken.exception.IdTokenInvalidException;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import jnpf.base.ActionResult;
import jnpf.base.ActionResultCode;
import jnpf.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;import java.util.List;
import java.util.Map;@Slf4j
@RestController
@RestControllerAdvice
public class ResultException {@ResponseBody@ExceptionHandler(value = LoginException.class)public ActionResult loginException(LoginException e) {return ActionResult.fail(ActionResultCode.Fail.getCode(), e.getMessage());}@ResponseBody@ExceptionHandler(value = ImportException.class)public ActionResult loginException(ImportException e) {return ActionResult.fail(ActionResultCode.Fail.getCode(), e.getMessage());}@ResponseBody@ExceptionHandler(value = DataException.class)public ActionResult dataException(DataException e) {return ActionResult.fail(ActionResultCode.Fail.getCode(), e.getMessage());}@ResponseBody@ExceptionHandler(value = RuntimeException.class)public ActionResult dataException(RuntimeException e) {return ActionResult.fail(ActionResultCode.Fail.getCode(), e.getMessage());}@ResponseBody@ExceptionHandler(value = IllegalArgumentException.class)public ActionResult dataException(IllegalArgumentException e) {return ActionResult.fail(ActionResultCode.Fail.getCode(), e.getMessage());}@ResponseBody@ExceptionHandler(value = WorkFlowException.class)public ActionResult workFlowException(WorkFlowException e) {if (e.getCode() == 200) {List<Map<String, Object>> list = JsonUtil.getJsonToListMap(e.getMessage());return ActionResult.success(list);} else {return ActionResult.fail(e.getMessage());}}@ResponseBody@ExceptionHandler(value = WxErrorException.class)public ActionResult wxErrorException(WxErrorException e) {return ActionResult.fail(e.getError().getErrorCode(), "操作过于频繁");}@ResponseBody@ExceptionHandler(NotPermissionException.class)public ActionResult<Void> handleNotPermissionException(NotPermissionException e) {return ActionResult.fail(ActionResultCode.Fail.getCode(), "没有访问权限,请联系管理员授权");}@ResponseBody@ExceptionHandler(NotRoleException.class)public ActionResult<Void> handleNotRoleException(NotRoleException e) {return ActionResult.fail(ActionResultCode.ValidateError.getCode(), "没有访问权限,请联系管理员授权");}@ResponseBody@ExceptionHandler(NotLoginException.class)public ActionResult<Void> handleNotLoginException(NotLoginException e) {return ActionResult.fail(ActionResultCode.SessionOverdue.getCode(), "认证失败,无法访问系统资源");}@ResponseBody@ExceptionHandler(IdTokenInvalidException.class)public ActionResult<Void> handleIdTokenInvalidException(IdTokenInvalidException e) {return ActionResult.fail(ActionResultCode.SessionOverdue.getCode(), "无效内部认证,无法访问系统资源");}
}
3. 创建统一错误响应体
package jnpf.base;import com.fasterxml.jackson.annotation.JsonInclude;import jnpf.base.vo.PageListVO;
import jnpf.base.vo.PaginationVO;
import jnpf.constant.MsgCode;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;import java.util.List;@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ActionResult<T> {@Schema(description = "状态码")private Integer code;@Schema(description = "返回信息")private String msg;@Schema(description = "返回数据")private T data;public static <T> ActionResult<T> success() {ActionResult<T> jsonData = new ActionResult<>();jsonData.setCode(200);jsonData.setMsg(MsgCode.SU000.get());return jsonData;}public static <T> ActionResult<T> success(String msg) {ActionResult<T> jsonData = new ActionResult<>();jsonData.setCode(200);jsonData.setMsg(msg);return jsonData;}public static <T> ActionResult<T> success(T object) {ActionResult<T> jsonData = new ActionResult<>();jsonData.setData(object);jsonData.setCode(200);jsonData.setMsg(MsgCode.SU000.get());return jsonData;}public static <T> ActionResult<T> success(String msg, T object) {ActionResult<T> jsonData = new ActionResult<>();jsonData.setData(object);jsonData.setCode(200);jsonData.setMsg(msg);return jsonData;}public static <T> ActionResult<T> fail(Integer code, String message) {ActionResult<T> jsonData = new ActionResult<>();jsonData.setCode(code);jsonData.setMsg(message);return jsonData;}public static ActionResult<String> fail(String msg, String data) {ActionResult<String> jsonData = new ActionResult<>();jsonData.setMsg(msg);jsonData.setData(data);return jsonData;}public static <T> ActionResult<T> fail(String msg) {ActionResult<T> jsonData = new ActionResult<>();jsonData.setMsg(msg);jsonData.setCode(400);return jsonData;}public static <T> ActionResult<PageListVO<T>> page(List<T> list, PaginationVO pagination) {ActionResult<PageListVO<T>> jsonData = new ActionResult<>();PageListVO<T> vo = new PageListVO<>();vo.setList(list);vo.setPagination(pagination);jsonData.setData(vo);jsonData.setCode(200);jsonData.setMsg(MsgCode.SU000.get());return jsonData;}public static <T> ActionResult<DataInterfacePageListVO<T>> page(List<T> list, PaginationVO pagination, String dataProcessing) {ActionResult<DataInterfacePageListVO<T>> jsonData = new ActionResult<>();DataInterfacePageListVO<T> vo = new DataInterfacePageListVO<>();vo.setList(list);vo.setPagination(pagination);vo.setDataProcessing(dataProcessing);jsonData.setCode(200);jsonData.setData(vo);jsonData.setMsg(MsgCode.SU000.get());return jsonData;}}
4. 异常错误提示枚举
public enum ActionResultCode {/*** 成功*/Success(200, "成功"),/*** 失败*/Fail(400, "失败"),/*** 验证错误*/ValidateError(401, "验证错误"),/*** 异常*/Exception(500, "异常"),/*** 登录过期提示*/SessionOverdue(600, "登录过期,请重新登录"),/*** 踢出提示*/SessionOffLine(601, "您的帐号在其他地方已登录,被强制踢出"),/*** token失效*/SessionError(602, "Token验证失败");private int code;private String message;ActionResultCode(int code, String message) {this.code = code;this.message = message;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}
}
关键点说明:
@ControllerAdvice
:标记为全局异常处理器@ExceptionHandler
:指定处理的异常类型响应实体:使用
ActionResult
可自定义HTTP状态码异常优先级:Spring会匹配最具体的异常处理器
错误信息:包含时间戳、错误码、消息和请求路径
这样实现的全局异常处理器具有以下优点:
统一处理控制器层所有异常
支持自定义异常类型和错误码
返回结构化的错误信息
分离业务逻辑与错误处理
支持HTTP状态码动态设置
易于扩展和维护