Springboot全局异常处理

发布时间 2023-11-23 10:09:53作者: chuimber

自定义异常枚举类

枚举类用于定义自定义的异常类型,对应自定义错误码,错误信息,状态码

public enum ErrorCodeEnum {
    USERINFO_EXCEPTION(10001,HttpStatus.INTERNAL_SERVER_ERROR,"UserInfo Exception"),
    AUTHORIZATION_EXCEPTION(10002,HttpStatus.UNAUTHORIZED,"Authorization Exception"),
    FILE_EXCEPTION(10003,HttpStatus.INTERNAL_SERVER_ERROR,"File Exception");
    private int code;
    private HttpStatus status;
    private String msg;
    ErrorCodeEnum(int code, HttpStatus status, String msg){
        this.code=code;
        this.status=status;
        this.msg=msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public HttpStatus getStatus() {
        return status;
    }

    @Override
    public String toString() {
        return "ErrorCodeEnum{" +
                "code=" + code +
                ", status=" + status +
                ", msg='" + msg + '\'' +
                '}';
    }
}

自定义异常类

自定义非受检异常类继承于RuntimeException=>Exceptiom=>Throwable,通过引入枚举类指定异常类型

public class CustomException extends RuntimeException{
    private ErrorCodeEnum errorCodeEnum;
    private int code;
    private String msg;
    private HttpStatus status;
    public CustomException(ErrorCodeEnum errorCodeEnum){
        this.errorCodeEnum=errorCodeEnum;
        this.code= errorCodeEnum.getCode();
        this.msg= errorCodeEnum.getMsg();
        this.status=errorCodeEnum.getStatus();

    }
    public CustomException(ErrorCodeEnum errorCodeEnum,String additionalMsg){
        this(errorCodeEnum);
        this.msg=this.msg+':'+additionalMsg;
    }

    public ErrorCodeEnum getErrorCodeEnum() {
        return errorCodeEnum;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public HttpStatus getStatus() {
        return status;
    }
	//重写父类getMessage方法,便于满足后期不同类型的异常类统一执行getMessage的需求
    @Override
    public String getMessage() {
        return this.msg;
    }
}

全局异常处理类

全局异常处理类对全局手动指定的错误类进行捕获,可以在捕获后处理、包装信息返回客户端,也可继续抛出异常通过AOP进行日志记录,也可在不继续抛出异常,返回客户端的同时,通过AOP的方法捕获进行异常信息的日志记录

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    /**
     * 异常捕获处理,将异常信息包装于自定义结果类,以json返回给客户端
     */
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<Result> handleFileException(CustomException ex){
        return new ResponseEntity<>(new Result(ex.getCode(), ex.getMsg()),ex.getStatus());
    }

    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity<Result> handlerRuntimeException(RuntimeException ex){
        return new ResponseEntity<>(new Result(10000, ex.getClass().getSimpleName()+':'+ex.getMessage()),HttpStatus.INTERNAL_SERVER_ERROR);

    }
}

自定义结果类

public class Result {

    private int code;
    private String msg;
    private Map<String,?> data;
    public Result(int code,String msg){
        this.code=code;
        this.msg=msg;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Map<String, ?> getData() {
        return data;
    }

    public Result setData(Map<String, ?> data) {
        this.data = data;
        return this;
    }

}

在上面的基础上通过AOP切入全局异常处理类,进行异常信息的日志记录

  • 依赖引入

    • pom.xml
    		<dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
                <exclusions>
                    <exclusion><!--排除默认日志框架-->
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-starter-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <!-- Spring AOP -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-aop</artifactId>
            </dependency>
    
            <!-- log4j2-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-log4j2</artifactId>
            </dependency>
    
    • 在src/main/resources下新建配置文件log4j2.xml

      <?xml version="1.0" encoding="UTF-8"?>
      <Configuration status="warn">
          <Appenders>
              <!-- 将日志输出到控制台 -->
              <Console name="Console" target="SYSTEM_OUT">
                  <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
              </Console>
          </Appenders>
          <Loggers>
              <Root level="INFO">
                  <AppenderRef ref="Console"/>
              </Root>
          </Loggers>
      </Configuration>
      
    • application.properties

      #logging
      logging.config=classpath:log4j2.xml
      
  • 全局异常类

    @ControllerAdvice
    @ResponseBody
    public class GlobalExceptionHandler {
    
        /**
         * 异常处理
         * AOP切入记录异常日志
         */
        @ExceptionHandler(CustomException.class)
        public ResponseEntity<Result> handleFileException(CustomException ex){
            return new ResponseEntity<>(new Result(ex.getCode(), ex.getMsg()),ex.getStatus());
        }
    
        @ExceptionHandler(RuntimeException.class)
        public ResponseEntity<Result> handlerRuntimeException(RuntimeException ex){
            return new ResponseEntity<>(new Result(10000, ex.getClass().getSimpleName()+':'+ex.getMessage()),HttpStatus.INTERNAL_SERVER_ERROR);
    
        }
    }
    
  • AOP配置类

    @Aspect
    @Component
    public class ExceptionAspect {
        private static final Logger logger = LogManager.getLogger(ExceptionAspect.class);
    
        //指定AOP切入的具体包和类
        @Pointcut(value = "execution(* com.example.space.Advice.GlobalExceptionHandler.*(..))")
        public void LogPointcut() {
    
        }
    
        //方法执行后触发
        @After("LogPointcut()")
        public void AfterLog(JoinPoint joinPoint) {
            //获取异常方法参数
            Object[] args = joinPoint.getArgs();
            // 获取异常发生的方法和位置信息
            for (Object arg : args) {
                if (arg instanceof RuntimeException) {
                    RuntimeException ex = (RuntimeException) arg;
    
                    // 获取异常信息
                    String exceptionMessage = ex.getMessage();
    
                    // 获取异常发生的方法和位置信息
                    StackTraceElement[] stackTrace = ex.getStackTrace();
                    String exceptionMethod = (stackTrace.length > 0) ? stackTrace[0].getMethodName() : "Unknown Method";
                    String exceptionLocation = (stackTrace.length > 0) ?
                            stackTrace[0].getClassName() + "." + exceptionMethod : "Unknown Location";
    
                    logger.error("Exception in method: " + exceptionLocation + ": " + exceptionMessage);
                }
    
            }
    
        }
    }