自学内容网 自学内容网

Spring Boot 全局异常处理

  在开发任何应用程序时,异常处理都是至关重要的。一个良好的异常处理机制不仅能提高用户体验,还能帮助开发者更好地定位和修复问题。Spring Boot 提供了强大的异常处理能力,使我们能够集中处理应用程序中抛出的各种异常,从而构建更健壮和可靠的系统。
  本文将深入探讨 Spring Boot 中全局异常处理的最佳实践,包括如何创建全局异常处理器,如何自定义异常响应,以及如何处理不同类型的异常。

为什么需要全局异常处理?

  1. 避免用户看到丑陋的错误页面:未处理的异常会导致用户看到默认的错误页面,这不仅不友好,还可能暴露敏感信息。
  2. 统一异常响应格式:通过全局异常处理,可以统一应用程序的异常响应格式,方便客户端解析和处理错误。
  3. 简化代码逻辑:将异常处理逻辑集中到一个地方,可以减少在各个业务逻辑代码中重复编写异常处理代码。
  4. 提高系统健壮性:全局异常处理可以捕获应用程序中未知的异常,避免程序崩溃。
  5. 方便日志记录和监控:将异常集中处理,方便记录日志和进行监控,帮助开发者更好地了解系统运行情况。

Spring Boot 中的全局异常处理:

  Spring Boot 提供了两个注解来实现全局异常处理:@ControllerAdvice@ExceptionHandler
@ControllerAdvice: 这个注解声明一个类为全局异常处理类,该类可以捕获所有 @Controller 中抛出的异常。
@RestControllerAdvice:作用于所有使用了 @RestController 或者 @Controller + 方法级别 @ResponseBody 注解的类。
@ExceptionHandler: 这个注解用于指定一个方法来处理特定的异常。

实践:创建全局异常处理器

1、定义自定义异常类: 首先,我们可以定义一些自定义异常类,用于表示业务上的异常,比如用户不存在,参数错误等

public class ServiceException extends RuntimeException{

    /**
     * 错误码
     */
    private Integer code;

    /**
     * 错误提示
     */
    private String message;

    public ServiceException(String message){
        this.message = message;
    }

    public ServiceException(Integer code, String message) {
        this.message = message;
        this.code = code;
    }

    public ServiceException(Status status, Object... statusParams){
        this.code = status.getCode();
        if(statusParams != null && statusParams.length > 0){
            this.message = MessageFormat.format(status.getMsg(), statusParams);
        } else {
            this.message = status.getMsg();
        }
    }

    public Integer getCode() {
        return code;
    }

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

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

}

2、创建全局异常处理类: 创建一个类,并使用 @ControllerAdvice 注解声明该类为全局异常处理类

import cn.hutool.core.util.ObjectUtil;
import com.extend.chk.domain.Result;
import com.extend.chk.domain.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;

@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 拦截业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public Result handleServiceException(ServiceException e, HttpServletRequest request){
        logger.error("请求地址:{}, 发生业务异常: {}", request.getRequestURI(), e.getMessage(), e);
        Integer code = e.getCode();
        String msg = e.getMessage();
        return ObjectUtil.isNotNull(code) ? Result.error(code, msg) : Result.error(msg);
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public Result handleRuntimeException(RuntimeException e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}',发生未知异常.", requestURI, e);
        return Result.error(e.getMessage());
    }

    /**
     * 拦截系统异常
     */
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e, HttpServletRequest request) {
        String requestURI = request.getRequestURI();
        logger.error("请求地址'{}',发生系统异常.", requestURI, e);
        return Result.error(e.getMessage());
    }

}
  1. @ExceptionHandler(ServiceException.class):指定handleServiceException方法处理 ServiceException类型的异常。
  2. @ExceptionHandler(RuntimeException.class):指定handleRuntimeException方法处理 RuntimeException类型的异常。
  3. @ExceptionHandler(Exception.class):指定 handleException方法处理所有未被特定处理的异常, 并记录日志。

3、Controller 中抛出异常: 在 Controller 中抛出自定义的异常,测试全局异常处理是否生效。

@RestController
@RequestMapping("/yes")
public class ExtendController extends BaseController {

    @GetMapping("/users/{id}")
    public Integer getUser(@PathVariable int id) {
        if (id == 1) {
            throw new ServiceException("业务异常");
        } else if (id == 2) {
            throw new RuntimeException("非检查型异常");
        } else {
            int a = 1 / 0;
            return a;
        }
    }
}
  1. 当 id 为 1 时,抛出 ServiceException异常。 {“code”: 10000,“msg”: “业务异常”,“data”: null}
  2. 当 id 为 2 时,抛出 RuntimeException异常。 {“code”: 10000,“msg”: “非检查型异常”,“data”: null}
  3. 当 id 为其他值时,抛出 Exception 异常。 {“code”: 10000,“msg”: “/ by zero”,“data”: null}

最佳实践

  1. 针对不同异常类型制定不同的处理策略: 不同的异常和前端约定不同的返回方式。
  2. 不要暴露敏感信息:在异常响应中,不要暴露系统内部的敏感信息,比如数据库连接字符串等。
  3. 记录日志: 将异常信息记录到日志中,方便问题追踪。
  4. 统一响应格式:保持一致的响应格式,方便客户端处理。
  5. 提供友好的错误信息: 提供给用户的错误信息应该易于理解,而不是技术术语。

原文地址:https://blog.csdn.net/I_Am_Your_God52/article/details/145185826

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!