SpringBoot 統一異常處理


1.前言

在做項目的時候,常常會遇到這些情況,

​ (一)、繁多的if..else 造成大量的參數代碼判斷,於是就在實體類字段中添加@NotBlank,@NotNull等注解代替,可是注解上自定義的message 消息無法規范的返回到前端;

​ (二)、在業務層代碼中,當方法層層嵌套,對最深處的代碼進行不滿足的參數做判斷時,直接返回響應體並不是很合適(這個時候就需要拋出自定義異常)

​ (三)、通過Assert 斷言去除冗余的if..else 時,發現斷言拋出的異常也沒有規范的返回前端;

​ 為了解決這些情況,我們需要做一個統一異常處理,無論是注解的異常拋出,自定義異常拋出、以及斷言的異常拋出都需要統一獲取其中的message。這個統一異常處理即今天所要說的 @ExceptionHandler+@ControllerAdvice ,統一異常處理。

2.直接實戰

原理也不解釋,廢話也不多說,直接記錄怎么配置,怎么使用。想知道原理的,自行百度。

2.1.創建項目

​ 通過IDEA 中的SpringInitializr 創建springboot項目,並一次創建各個層的文件夾,controller、service、entity、model、exception、handler等文件夾。如下所示:

2.2 統一異常配置

pom.xml 添加 以下依賴

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.7</version>
</dependency>

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.1.Final</version>
</dependency>

2.2.1 Result 響應實體類,用來返回前端的實體類。

/**
 * @Author: DZBiao
 * @Date : 2021/12/11
 * @Description : 描述:
 **/
public class Result {

    /**
     * 響應狀態碼
     */
    private Integer code;
    /**
     * 響應成功與否
     */
    private boolean success;
    /**
     * 響應消息
     */
    private String msg;
    /**
     * 響應數據
     */
    private Object data;

    public Integer getCode() {
        return code;
    }

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

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getMsg() {
        return msg;
    }

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

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Result() {
    }

    public Result(Integer code, boolean success, String msg) {
        this.code = code;
        this.success = success;
        this.msg = msg;
    }

    public Result(Integer code, boolean success, String msg, Object data) {
        this.code = code;
        this.success = success;
        this.msg = msg;
        this.data = data;
    }

    /**
     * 成功 返回默認成功信息
     *
     * @return
     */
    public static Result SUCCESS() {
        return new Result(1, true, "操作成功", null);
    }

    /**
     * 成功 返回(data數據)成功信息
     *
     * @param data
     * @return
     */
    public static Result SUCCESS(Object data) {
        return new Result(1, true, "操作成功", data);
    }

    /**
     * 成功 返回自定義(消息、data數據)成功信息
     *
     * @param msg
     * @param data
     * @return
     */
    public static Result SUCCESS(String msg, Object data) {
        return new Result(1, true, msg, data);
    }

    /**
     * 失敗 返回默認失敗信息
     *
     * @return
     */
    public static Result ERROR() {
        return new Result(-1, false, "操作失敗", null);
    }

    /**
     * 失敗 返回自定義(消息)失敗信息
     *
     * @param msg
     * @return
     */
    public static Result ERROR(String msg) {
        return new Result(-1, false, msg, null);
    }

    /**
     * 失敗 返回自定義(消息、狀態碼)失敗信息
     *
     * @param code
     * @param msg
     * @return
     */
    public static Result ERROR(Integer code, String msg) {
        return new Result(code, false, msg, null);
    }
}

2.2.2 @Controller 和 @RestControllerAdvice 配置

在controller層中新建IndexController類,傳參實體類使用簡單的User類;

@RestController
@RequestMapping("/index")
public class IndexController {


    @PostMapping("/list")
    public Result index(@Valid @RequestBody User user) throws Exception {

        return Result.SUCCESS();

    }

}

實體類User ,當我想要在傳參時進行非空判斷,則添加@NotBlank注解,並在Controller層 添加@Valid和@RequestBody 注解。(@Data 注解為Lombok 插件,idea中安裝,如果沒有直接在實體類中生成get和set 方法代替.)

@Data
public class User {

    private String id ;         // ID

    @NotBlank(message = "用戶名不能為空.")
    private String username ;   // 用戶名

    @NotBlank(message = "性別不能為空")
    private String sex ;  // 性別

    @NotBlank(message = "地址不能為空")
    private String address ;    // 地址

    private String status ;


}

在handler 文件夾中新建GlobalExceptionController類,並配置@RestControllerAdvice注解。對於實體類參數上的注解的異常捕獲,我們在統一異常處理類中,通過MethodArgumentNotValidException進行捕獲處理。

@RestControllerAdvice
public class GlobalExceptionController {
	
    
        /**
     * 處理方法參數異常,如 實體類上的@NotBlank注解異常,@NotNull注解等異常信息返回。
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentException(MethodArgumentNotValidException  ex){

        return Result.ERROR(ex.getBindingResult().getFieldError().getDefaultMessage()) ;
    }
}

至此我們解決了三個問題中的其中一個問題。下面解決拋出自定義異常 和斷言異常的捕獲。

在GlobalExceptionController中再添加 處理自定義異常和斷言異常的捕獲方法。

@RestControllerAdvice
public class GlobalExceptionController {
	
    
        /**
     * 處理方法參數異常,如 實體類上的@NotBlank注解異常,@NotNull注解等異常信息返回。
     * @param ex
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleMethodArgumentException(MethodArgumentNotValidException  ex){

        return Result.ERROR(ex.getBindingResult().getFieldError().getDefaultMessage()) ;
    }
    
    
        /**
     * 斷言、自定義異常等處理
     * @param ex
     * @return
     */
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception  ex){

        return Result.ERROR(ex.getMessage()) ;
    }
   
}

3.測試

我們在exception文件夾中創建自定義異常類:CustomException,並繼承RuntimeException

public class CustomException extends RuntimeException {

    private String message ;

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

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

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

在controller中添加一些判斷進行觀察,看看我們配置的情況。

@RestController
@RequestMapping("/index")
public class IndexController {


    @PostMapping("/list")
    public Result index(@Valid @RequestBody User user) throws Exception {

        // 拋出Exception異常
        if (user.getUsername().equals("xxx")){
            throw new Exception("用戶名錯誤.");
        }

        // 自定義異常
        if (user.getUsername().equals("xxxxx")){
            throw new CustomException("自定義異常");
        }

        // 斷言異常
        Assert.notNull(user.getStatus(), "狀態不能為空");

        return Result.SUCCESS();

    }

}

我們通過Postman或者ApiPost觀察。當我們什么參數都不傳時,就會捕獲注解上的異常message,當username傳“xxx”,就會捕獲用戶名錯誤.異常,同理,可以觀察到其他的情況,不多贅述。

統一異常處理配置很簡單。至此配置完成。

通過統一異常處理的配置,我們可以去除大量的if..else 冗余代碼,全部交由注解或者Assert斷言,或者自定義異常來解決。

需要注意的一點是:該@Valid 注解 和@NotBlank 注解 是在springboot3.6以下版本基礎之上的。本地demo使用的版本是2.6.1,springboot3.6 需要使用 @Validated 注解 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM