全局异常拦截器
发表于更新于
字数总计:2k阅读时长:8分钟阅读量: 剑河
后端Java全局异常拦截器
苏泽怎么个全局异常呢?
先了解其注解
@RestControllerAdvice 其作用是为了拦截带有@Controller注解控制层内的异常
比如还要service层,还要数据持久层。他们属于同一层,但是控制层是调用服务层,服务层会调用数据持久层,因此,无论是服务层,还是数据持久层发生异常,捕获到了,都会在控制层,全局异常拦截器给捕获到。
throw 捕获到异常后 会返回给调用此方法的调用者返回上一层?
基础不太好突然想到为什么返回JSON给前端,
突然想到带有***@RespondBody***
所以什么是全局异常拦截器?
根据Spring提供的注解@RestControllerAdvice
1 2 3 4 5 6 7 8
| @Componet @Slf4j @RestControllerAdvice public class GlobalExceptionInterceptor{ @SneakyThrows @ExceptionHandler(value = MethodArgumentNotValidException.class) }
|
BindingResult
是Spring Framework 中用于处理表单验证错误的一个类,与@Valid 或@Validated注解一起使用通常用于捕获Java Bean 验证中的错误
主要方法
hasErrors(): 检查是否存在任何验证错误。
getFieldErrors(): 获取与字段相关的验证错误。
getGlobalErrors(): 获取与整个对象相关的验证错误(例如全局约束)。
总结
BindingResult 不是单独存在的,它是与 @Valid 或 @Validated 注解一起使用的。它用于捕获和处理验证过程中产生的错误。在控制器中,BindingResult 允许你在处理验证失败时进行自定义的错误处理,而在全局异常处理器中,它可以帮助你将验证错误格式化为适当的响应返回给客户端。这使得处理和返回验证错误变得更加灵活和一致。
Optional 是Java8引入的一个工具,用于处理可能为null的值,可以帮助避免空指针异常,提供更清晰的代码结构,并支持函数式编程风格的操作,使代码更加健壮性和可读。
1 2 3 4 5 6 7 8
| Optional<String> getName(User user) { return Optional.ofNullable(user) .map(User::getProfile) .map(Profile::getName) .orElse("Unknown"); }
|
案例
1 2 3 4 5 6 7 8 9 10
| String result = Optional.ofNullable(fileName()) .map(String::trim) .filter(name -> name.length()>5) .orElse("null"); System.out.println(result);
private static String fileName() { String name = " 1234 "; return name; }
|

如果我改为 name.length()>3的话则显示1234
回到正题
枚举总结
枚举是 Java 提供的一种特殊数据类型,用于定义一组固定的常量。它可以包含字段、方法和构造函数,并支持多种高级用法,如实现接口和定义自定义行为。枚举使代码更加可读、可维护,并提供了编译时的类型安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public enum Day { MONDAY("星期一"), TUESDAY("星期二"), WEDNESDAY("星期三"), THURSDAY("星期四"), FRIDAY("星期五"), SATURDAY("星期六"), SUNDAY("星期天");
private final String Date;
Day(String date) { this.Date = date; }
public String getDate() { return Date; } }
|
通过Day.MONDAY.getDate()获取 星期一的信息。
首先定义了三个自定义异常类
定义了一个抽象类
AbstractException 继承运行时异常RuntimeException
ClientException
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public class ClientException extends AbstractException {
public ClientException(IErrorCode errorCode) { this(null, null, errorCode); }
public ClientException(String message) { this(message, null, BaseErrorCode.CLIENT_ERROR); }
public ClientException(String message, IErrorCode errorCode) { this(message, null, errorCode); }
public ClientException(String message, Throwable throwable, IErrorCode errorCode) { super(message, throwable, errorCode); }
@Override public String toString() { return "ClientException{" + "code='" + errorCode + "'," + "message='" + errorMessage + "'" + '}'; } }
|
RemoteException
ServiceException
以及枚举的错误码定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| package com.gues.shortLink.admin.common.convention.errorcode;
public enum BaseErrorCode implements IErrorCode {
CLIENT_ERROR("A000001", "用户端错误"),
USER_REGISTER_ERROR("A000100", "用户注册错误"), USER_NAME_VERIFY_ERROR("A000110", "用户名校验失败"), USER_NAME_EXIST_ERROR("A000111", "用户名已存在"), USER_NAME_SENSITIVE_ERROR("A000112", "用户名包含敏感词"), USER_NAME_SPECIAL_CHARACTER_ERROR("A000113", "用户名包含特殊字符"), USER_NOT_EXIST("A000114","用户不存在"), PASSWORD_VERIFY_ERROR("A000120", "密码校验失败"), PASSWORD_SHORT_ERROR("A000121", "密码长度不够"), PHONE_VERIFY_ERROR("A000151", "手机格式校验失败"),
IDEMPOTENT_TOKEN_NULL_ERROR("A000200", "幂等Token为空"), IDEMPOTENT_TOKEN_DELETE_ERROR("A000201", "幂等Token已被使用或失效"),
FLOW_LIMIT_ERROR("A000300", "当前系统繁忙,请稍后再试"),
SERVICE_ERROR("B000001", "系统执行出错"), SERVICE_TIMEOUT_ERROR("B000100", "系统执行超时"),
REMOTE_ERROR("C000001", "调用第三方服务出错");
private final String code;
private final String message;
BaseErrorCode(String code, String message) { this.code = code; this.message = message; }
@Override public String code() { return code; }
@Override public String message() { return message; } }
|
实现一个接口
IEooroCode类
1 2 3 4
| public interimple IErrorCode{ String code(); String message(); }
|
建一个全局异常拦截器的类加注解
@RestControllerAdvice
@Compoent
@Slf4j
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| package com.gues.shortLink.admin.common.web;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.gues.shortLink.admin.common.convention.errorcode.BaseErrorCode; import com.gues.shortLink.admin.common.convention.execption.AbstractException; import com.gues.shortLink.admin.common.convention.execption.ClientException; import com.gues.shortLink.admin.common.convention.result.Result; import com.gues.shortLink.admin.common.convention.result.Results; import jakarta.servlet.http.HttpServletRequest; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.Optional;
@Component @Slf4j @RestControllerAdvice public class GlobalExceptionHandler {
@SneakyThrows @ExceptionHandler(value = MethodArgumentNotValidException.class) public Result validExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) { BindingResult bindingResult = ex.getBindingResult(); FieldError firstFieldError = CollectionUtil.getFirst(bindingResult.getFieldErrors()); String exceptionStr = Optional.ofNullable(firstFieldError) .map(FieldError::getDefaultMessage) .orElse(StrUtil.EMPTY); log.error("[{}] {} [ex] {}", request.getMethod(), getUrl(request), exceptionStr); return Results.failure(BaseErrorCode.CLIENT_ERROR.code(), exceptionStr); }
@ExceptionHandler(value = {AbstractException.class}) public Result abstractException(HttpServletRequest request, AbstractException ex) { if (ex.getCause() != null) { log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString(), ex.getCause()); return Results.failure(ex); } log.error("[{}] {} [ex] {}", request.getMethod(), request.getRequestURL().toString(), ex.toString()); return Results.failure(ex); }
@ExceptionHandler(value = Throwable.class) public Result defaultErrorHandler(HttpServletRequest request, Throwable throwable) { log.error("[{}] {} ", request.getMethod(), getUrl(request), throwable); return Results.failure(); } private String getUrl(HttpServletRequest request) { if (StringUtils.isEmpty(request.getQueryString())) { return request.getRequestURL().toString(); } return request.getRequestURL().toString() + "?" + request.getQueryString(); } }
|
建议每一行都会看懂 ,以为我以及把基础在前面已经讲了,最后还要返回类型我在后面补上。
返回异常到前台。
1 2 3 4 5 6 7 8 9
| public static Result<Void> failure(AbstractException abstractException) { String errorCode = Optional.ofNullable(abstractException.getErrorCode()) .orElse(BaseErrorCode.SERVICE_ERROR.code()); String errorMessage = Optional.ofNullable(abstractException.getErrorMessage()) .orElse(BaseErrorCode.SERVICE_ERROR.message()); return new Result<Void>() .setCode(errorCode) .setMessage(errorMessage); }
|
Result
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package com.gues.shortLink.admin.common.convention.result;
import lombok.Data; import lombok.experimental.Accessors;
import java.io.Serial; import java.io.Serializable;
@Data @Accessors(chain = true) public class Result<T> implements Serializable {
@Serial private static final long serialVersionUID = 5679018624309023727L;
public static final String SUCCESS_CODE = "0";
private String code;
private String message;
private T data;
private String requestId;
public boolean isSuccess() { return SUCCESS_CODE.equals(code); } }
|
必须理通!
总结
@RestControllerAdvice 拦截控制层的异常
@ResponBody 返回JSON包装
@SneakyThrows注解 相当于 我要在方法中声明throw的异常,如果我加的话就不需要啦
BindResult
1 2
| BindingResult bindingResult = ex.getBindingResult(); FieldError firstFieldError = CollectionUtil.getFirst(bindingResult.getFieldErrors());
|
枚举 枚举是 Java 提供的一种特殊数据类型,用于定义一组固定的常量
Optional 是一个容器 预防空指针的最佳良器 Java 8 推出的
1 2 3 4
| String result = Optional.ofNullable(findName()) .map(String::trim()) .filter(name -> name.length()>3) .orElse("null")
|