天天看點

Spring boot 自定義全局異常處理

在日常項目開發中,異常是常見的,但是如何更高效的處理好異常資訊,讓我們能快速定位到BUG,是很重要的,不僅能夠提高我們的開發效率,還能讓你代碼看上去更舒服,SpringBoot的項目已經對有一定的異常處理了,但是對于我們開發者而言可能就不太合适了,是以我們需要對這些異常進行統一的捕獲并處理。

@ControllerAdvice

注解是Spring3.2中新增的注解,學名是Controller增強器,作用是給Controller控制器添加統一的操作或處理。結合方法型注解

@ExceptionHandler

,用于捕獲Controller中抛出的指定類型的異常,進而達到不同類型的異常差別處理的目的。

補充:

        @RestControllerAdvice 該注解其實是 

@ControllerAdvice

 + 

@ResponseBody

的組合注解,其中

@ControllerAdvice

 包含 

@Component

,是以被該注解修飾的Java類也是一個被Spring 管理的Bean。

        @ReponseStatus 注解:設定響應狀态碼

        假設抛出異常不是我們自定義的異常,我們想改變響應的狀态碼,通過@ExceptionHandler來處理異常,并在@ExceptionHandler方法上也可以設定@ResponseStatus來達到效果;

         假如抛出自定義的異常,自己沒有定義異常處理界面,那在異常上标注@ResponseStatus就可以走 伺服器預設的界面展示,或者通過web.xml 配置error-code \ error-page來自定義界面處理異常;

自定義全局異常核心共有4步:

自定義全局異常
	1. 統一的傳回類型
	2. 定義枚舉類型code與錯誤資訊 enum
	3. 自定義業務異常類,實作runtimeException接口
	4. 定義全局異常處理類
           

pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>ExceptionGlobal</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ExceptionGlobal</name>
    <description>ExceptionGlobal</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.4</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.3.7.RELEASE</version>
                <configuration>
                    <mainClass>com.example.exceptionglobal.ExceptionGlobalApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>
           

 controller:

package com.example.exceptionglobal.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class myController {

    @RequestMapping("/get")
    public int getException(){
        int i = 40 / 0;
        return 0;
    }
}
           

核心實作:

1. 基礎服務接口類+自定義統一傳回異常類

package com.example.exceptionglobal.result;

/**
 * @description: 服務接口類
 */
public interface BaseErrorInfoInterface {

    /**
     *  錯誤碼
     * @return
     */
    String getResultCode();

    /**
     * 錯誤描述
     * @return
     */
    String getResultMsg();
}
           
package com.example.exceptionglobal.result;

import com.alibaba.fastjson.JSONObject;
import com.example.exceptionglobal.enums.ExceptionEnum;

/**
 * 自定義統一傳回類型
 * 這裡的統一傳回指的是自定義異常的統一傳回類型,不是controller接口的傳回類型
 */
public class ResultResponse {

    private static final long serialVersionUID  = -8713837118340960775L;
    /**
     * 響應代碼
     */
    private String code;

    /**
     * 響應消息
     */
    private String message;

    /**
     * 響應結果
     */
    private Object result;

    public ResultResponse() {
    }

    public ResultResponse(BaseErrorInfoInterface errorInfo) {
        this.code = errorInfo.getResultCode();
        this.message = errorInfo.getResultMsg();
    }

    public String getCode() {
        return code;
    }

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

    public String getMessage() {
        return message;
    }

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

    public Object getResult() {
        return result;
    }

    public void setResult(Object result) {
        this.result = result;
    }

    /**
     * 成功
     *
     * @return
     */
    public static ResultResponse success() {
        return success(null);
    }

    /**
     * 成功
     * @param data
     * @return
     */
    public static ResultResponse success(Object data) {
        ResultResponse rb = new ResultResponse();
        rb.setCode(ExceptionEnum.SUCCESS.getResultCode());
        rb.setMessage(ExceptionEnum.SUCCESS.getResultMsg());
        rb.setResult(data);
        return rb;
    }

    /**
     * 失敗
     */
    public static ResultResponse error(BaseErrorInfoInterface errorInfo) {
        ResultResponse rb = new ResultResponse();
        rb.setCode(errorInfo.getResultCode());
        rb.setMessage(errorInfo.getResultMsg());
        rb.setResult(null);
        return rb;
    }

    /**
     * 失敗
     */
    public static ResultResponse error(String code, String message) {
        ResultResponse rb = new ResultResponse();
        rb.setCode(code);
        rb.setMessage(message);
        rb.setResult(null);
        return rb;
    }

    /**
     * 失敗
     */
    public static ResultResponse error( String message) {
        ResultResponse rb = new ResultResponse();
        rb.setCode("-1");
        rb.setMessage(message);
        rb.setResult(null);
        return rb;
    }

    @Override
    public String toString() {
        return JSONObject.toJSONString(this);
    }

}
           

2. 異常處理枚舉類

package com.example.exceptionglobal.enums;

import com.example.exceptionglobal.result.BaseErrorInfoInterface;

/**
 * @description: 異常處理枚舉類
 */
public enum ExceptionEnum implements BaseErrorInfoInterface {

    // 資料操作錯誤定義
    SUCCESS("2000", "成功!"),
    BODY_NOT_MATCH("4000","請求的資料格式不符!"),
    SIGNATURE_NOT_MATCH("4001","請求的數字簽名不比對!"),
    NOT_FOUND("4004", "未找到該資源!"),
    INTERNAL_SERVER_ERROR("5000", "伺服器内部錯誤!"),
    SERVER_BUSY("5003","伺服器正忙,請稍後再試!");

    /**
     * 錯誤碼
     */
    private final String resultCode;

    /**
     * 錯誤描述
     */
    private final String resultMsg;

    ExceptionEnum(String resultCode, String resultMsg) {
        this.resultCode = resultCode;
        this.resultMsg = resultMsg;
    }

    @Override
    public String getResultCode() {
        return resultCode;
    }

    @Override
    public String getResultMsg() {
        return resultMsg;
    }
}
           

3. 自定義異常類

package com.example.exceptionglobal.exception;

import com.example.exceptionglobal.result.BaseErrorInfoInterface;

/**
 * @description: 自定義異常類
 */
public class BizException extends RuntimeException{

    private static final long serialVersionUID = 1L;

    /**
     * 錯誤碼
     */
    protected String errorCode;
    /**
     * 錯誤資訊
     */
    protected String errorMsg;

    public BizException() {
        super();
    }

    public BizException(BaseErrorInfoInterface errorInfoInterface) {
        super(errorInfoInterface.getResultCode());
        this.errorCode = errorInfoInterface.getResultCode();
        this.errorMsg = errorInfoInterface.getResultMsg();
    }

    public BizException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) {
        super(errorInfoInterface.getResultCode(), cause);
        this.errorCode = errorInfoInterface.getResultCode();
        this.errorMsg = errorInfoInterface.getResultMsg();
    }

    public BizException(String errorMsg) {
        super(errorMsg);
        this.errorMsg = errorMsg;
    }

    public BizException(String errorCode, String errorMsg) {
        super(errorCode);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }

    public BizException(String errorCode, String errorMsg, Throwable cause) {
        super(errorCode, cause);
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
    }


    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }

    @Override
    public Throwable fillInStackTrace() {
        return this;
    }
}
           

4. 自定義異常全局處理(核心中的核心!)

package com.example.exceptionglobal.exception;

import com.example.exceptionglobal.enums.ExceptionEnum;
import com.example.exceptionglobal.result.ResultResponse;
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;

/**
 * @description: 自定義異常全局處理
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

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

    /**
     * 處理自定義的業務異常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value = BizException.class)
    public ResultResponse bizExceptionHandler(HttpServletRequest req, BizException e){
        logger.error(("發生業務異常!原因是:{}"),e.getErrorMsg());
        return ResultResponse.error(e.getErrorCode(),e.getErrorMsg());
    }

    /**
     * 處理空指針的異常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =NullPointerException.class)
    public ResultResponse exceptionHandler(HttpServletRequest req, NullPointerException e){
        logger.error("發生空指針異常!原因是:",e);
        return ResultResponse.error(ExceptionEnum.BODY_NOT_MATCH);
    }

    /**
     * 處理其他異常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =Exception.class)
    public ResultResponse exceptionHandler(HttpServletRequest req, Exception e){
        logger.error("未知異常!原因是:",e);
        return ResultResponse.error(ExceptionEnum.INTERNAL_SERVER_ERROR);
    }
}
           

測試通路controller接口,手動觸發異常:

2022-11-23 12:37:32.207 ERROR 12172 --- [nio-8080-exec-1] c.e.e.exception.GlobalExceptionHandler   : 未知異常!原因是:

java.lang.ArithmeticException: / by zero
at com.example.exceptionglobal.controller.myController.getException(myController.java:11) ~[classes/:na]
           

至此結束!

參考連結:

Spring Boot項目優雅的全局異常處理方式(全網最新)_DT辰白的部落格-CSDN部落格_全局異常

Spring boot 全局異常處理_LvQiFen的部落格-CSDN部落格