Springboot联结万物学习笔记--Springboot微服务基础搭建篇(六)--SpringBoot全局异常处理

博客说明:撰写博客目的是在记录自己所学知识、在工作中使用技术遇到的技术问题、一些技术感悟,因此避免不了涉及到和其他文章有相似之处。本文从作者自己的实践中指出相关踩坑问题,着重指出学习过程中遇到的相关问题。如果存在相关侵权问题请联系博主删除,同时有技术上的见解可以在评论去里发出,会不定期回复,谢谢。

gitee地址:https://gitee.com/woniurunfast/springbootwitheverything

01背景

在企业SpringBoot的日常开发中,代码常常会遇到bug,为了方便对接调试等,要做统一异常处理

02目标

1、实现全局异常处理机制

03全局异常设置步骤

(1)统一封装异常处理枚举类

package com.hkx.demo.exception;

import lombok.Getter;

/**
 *
 */
@Getter
public enum ResultCodeEnum {

    UNKNOWN_REASON(false, 20001, "未知错误"),
    SERVER_ERROR(false, 500, "服务器忙,请稍后在试"),
    ORDER_CREATE_FAIL(false, 601, "订单下单失败");

    private Boolean success;
    private Integer code;
    private String message;
    private ResultCodeEnum(Boolean success, Integer code, String message) {
        this.success = success;
        this.code = code;
        this.message = message;
    }
}
package com.hkx.demo.exception;

import lombok.*;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class ErrorHandler {

    // 异常的状态码,从枚举中获得
    private Integer status;
    // 异常的消息,写用户看得懂的异常,从枚举中得到
    private String message;
    // 异常的名字
    private String exception;

    /**
     * 对异常处理进行统一封装
     *
     * @param resultCodeEnum 异常枚举
     * @param throwable 出现异常
     * @param message 异常的消息 null /by zero
     * @return
     */
    public static ErrorHandler fail(ResultCodeEnum resultCodeEnum, Throwable throwable, String message) {
        ErrorHandler errorHandler = ErrorHandler.fail(resultCodeEnum, throwable);
        errorHandler.setMessage(message);
        return errorHandler;
    }
    /**
     * 对异常枚举进行封装
     *
     * @param resultCodeEnum
     * @param throwable
     * @return
     */
    public static ErrorHandler fail(ResultCodeEnum resultCodeEnum, Throwable throwable) {
        ErrorHandler errorHandler = new ErrorHandler();
        errorHandler.setMessage(resultCodeEnum.getMessage());
        errorHandler.setStatus(resultCodeEnum.getCode());
        errorHandler.setException(throwable.getClass().getName());
        return errorHandler;
    }

}

(2)封装异常结果处理:

package com.hkx.demo.exception;

import lombok.*;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
public class ErrorHandler {

    // 异常的状态码,从枚举中获得
    private Integer status;
    // 异常的消息,写用户看得懂的异常,从枚举中得到
    private String message;
    // 异常的名字
    private String exception;

    /**
     * 对异常处理进行统一封装
     *
     * @param resultCodeEnum 异常枚举
     * @param throwable 出现异常
     * @param message 异常的消息 null /by zero
     * @return
     */
    public static ErrorHandler fail(ResultCodeEnum resultCodeEnum, Throwable throwable, String message) {
        ErrorHandler errorHandler = ErrorHandler.fail(resultCodeEnum, throwable);
        errorHandler.setMessage(message);
        return errorHandler;
    }
    /**
     * 对异常枚举进行封装
     *
     * @param resultCodeEnum
     * @param throwable
     * @return
     */
    public static ErrorHandler fail(ResultCodeEnum resultCodeEnum, Throwable throwable) {
        ErrorHandler errorHandler = new ErrorHandler();
        errorHandler.setMessage(resultCodeEnum.getMessage());
        errorHandler.setStatus(resultCodeEnum.getCode());
        errorHandler.setException(throwable.getClass().getName());
        return errorHandler;
    }

}

(3)在配置类定义全局异常处理器:

package com.hkx.demo.config;

import com.hkx.demo.exception.BusinessException;
import com.hkx.demo.exception.ErrorHandler;
import com.hkx.demo.exception.ResultCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {


    @ExceptionHandler(Throwable.class)
    public ErrorHandler makeExcepton(Throwable e, HttpServletRequest request){
        log.error("请求的地址是:{},出现的异常是:{}", request.getRequestURL(), e);
        return ErrorHandler.fail(ResultCodeEnum.SERVER_ERROR,e);
    }

    /**
     * 对服务器端出现500异常进行统一处理
     * 缺点:明确异常信息
     */
    @ExceptionHandler(BusinessException.class)
    public ErrorHandler makeOrderException(BusinessException businessException, HttpServletRequest request) {
        ErrorHandler errorHandler = ErrorHandler.builder()
                .message(businessException.getMessage())
                .status(businessException.getCode())
                .build();
        log.error("请求的地址是:{},出现的异常是:{}", request.getRequestURL(), businessException);
        return errorHandler;
    }
}

其中第二个方法扫描的师BusinessException类是自定义异常类,集成RuntimeException

package com.hkx.demo.exception;
import lombok.Data;


@Data
public class BusinessException extends RuntimeException {
    private Integer code;
    private String message;
    public BusinessException(ResultCodeEnum resultCodeEnum) {
        this.code = resultCodeEnum.getCode();
        this.message = resultCodeEnum.getMessage();
    }
    public BusinessException(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

测试异常处理

创建异常处理接口访问控制层:

package com.hkx.demo.controller;

import com.hkx.demo.entity.WoNiu;
import com.hkx.demo.exception.BusinessException;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/error")
@Slf4j
@Api("异常返回结果")
public class ErrorController {

    @GetMapping("/error1")
    public WoNiu error1(Integer id) {
        if (id.equals(1)) {
            throw new RuntimeException("run exception!");
        }
        WoNiu user = new WoNiu();
        user.setId(1);
        user.setDesc("yyds");
        user.setHobby("451212");
        user.setType("kkk");
        return user;
    }

    @GetMapping("/error2")
    public WoNiu error2(Integer id) {
        if (id.equals(1)) {
            throw new BusinessException(501,"参数不对");
        }
        WoNiu user = new WoNiu();
        user.setId(1);
        user.setDesc("yyds");
        user.setHobby("451212");
        user.setType("kkk");
        return user;
    }


    @GetMapping("/error3")
    public WoNiu error3(Integer id) {

        int i=1/0;

        WoNiu user = new WoNiu();
        user.setId(1);
        user.setDesc("yyds");
        user.setHobby("451212");
        user.setType("kkk");
        return user;
    }

}

上述三个接口是不同的触发异常访问的方法

error1:
image
error2:
image
error3:
image

统一异常和统一结果返回结合处理:

在config包下创建ResultResponseHandler类:

package com.hkx.demo.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hkx.demo.common.Result;
import com.hkx.demo.exception.ErrorHandler;
import lombok.SneakyThrows;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Type;

@ControllerAdvice(basePackages = "com.hkx.demo")
public class ResultResponseHandler implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

    // 参数o 代表其实就是springmvc的请求的方法的结果
    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        // 对请求的结果在这里统一返回和处理
        if (o instanceof ErrorHandler) {
            // 1、如果返回的结果是一个异常的结果,就把异常返回的结构数据倒腾到R.fail里面即可
            ErrorHandler errorHandler = (ErrorHandler) o;
            return Result.fail(errorHandler.getStatus(), errorHandler.getMessage());
        } else if (o instanceof String) {
            // 2、因为springmvc数据转换器对String是有特殊处理 StringHttpMessageConverter
            ObjectMapper objectMapper = new ObjectMapper();
            Result r = Result.success(o);
            return objectMapper.writeValueAsString(r);
        }
        return Result.success(o);
    }
}

这样无论成功或失败都会自动封装返回。

posted @ 2021-07-04 16:29  woniurunfast  阅读(86)  评论(0编辑  收藏  举报