統一返回物件和異常處理(一)

席潤發表於2019-02-12
統一返回物件
package com.demo1.rsponse;

public class Result<T> {

    /** 錯誤碼. */
    private Integer code;

    /** 提示資訊. */
    private String msg;

    /** 具體的內容. */
    private T data;

    public Integer getCode() {
        return code;
    }

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

    public String getMsg() {
        return msg;
    }

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

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
複製程式碼
package com.demo1.response;

import com.demo1.response.Result;

public class ResultUtil {

    public static Result success(Object object) {
        Result result = new Result();
        result.setCode(1);
        result.setMsg("成功");
        result.setData(object);
        return result;
    }

    public static Result success() {
        return success(null);
    }

    public static Result error(Integer code, String msg) {
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
}
複製程式碼
統一異常資訊及處理

我們想要統一異常處理,首先,需要定義一個自己的異常類來統一對外的異常展示;異常展示一般需要錯誤碼errCode和錯誤資訊errMsg,為了方便統一管理它們,我們會再來一個列舉類來定義它們;最後我們再來一個類捕獲並統一處理異常,這樣就能比較完美的實現異常的統一處理。在業務邏輯的實現中使用的話就是直接拋我們自己定義的異常。

下面來一個demo,自定義類繼承了RuntimeException,統一異常處理使用了Spring的@ControllerAdvice和@ExceptionHandler註解來實現,並通過日誌記錄下非執行時異常資訊。

自定義異常類
import com.demo1.error.EmBusinessError;

public class BusinessException extends RuntimeException{//使用RuntimeException的原因為spring框架只對丟擲的RuntimeException異常進行事務回滾

    private Integer code;

    public BusinessException(EmBusinessError emBusinessError) {
        super(emBusinessError.getMsg());
        this.code = emBusinessError.getCode();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}
複製程式碼

RuntimeException的一個構造方法

事務回滾

在spring的事務管理環境下,使用unckecked exception(未檢查異常)可以極大地簡化異常的處理,只需要在事務層宣告可能丟擲的異常(這裡的異常可以是自定義的unckecked exception體系),在所有的中間層都只是需要簡單throws即可,不需要捕捉和處理,直接到最高層,比如UI層再進行異常的捕捉和處理

在service類前加上@Transactional,宣告這個service所有方法需要事務管理。每一個業務方法開始時都會開啟一個事務。

Spring預設情況下會對執行期例外(RunTimeException)進行事務回滾。這個例外是unchecked(未檢查異常),如果遇到checked(檢查異常)例外就不回滾。

如何改變預設規則:

1 讓checked例外(檢查異常)也回滾:在整個方法前加上 @Transactional(rollbackFor=Exception.class)

2 讓unchecked例外(未檢查異常)不回滾: @Transactional(notRollbackFor=RunTimeException.class)

3 不需要事務管理的(只查詢的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)

注意: 如果異常被try{}catch{}了,事務就不回滾了,如果想讓事務回滾必須再往外拋try{}catch{throw Exception}。

放置錯誤碼和錯誤資訊的列舉類
public enum EmBusinessError {
    UNKONW_ERROR(-1, "未知錯誤"),
    SUCCESS(1, "成功"),
    PRIMARY_SCHOOL(100, "你可能還在上小學"),
    MIDDLE_SCHOOL(101, "你可能還在上初中"),

    ;

    private Integer code;

    private String msg;

    EmBusinessError(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}
複製程式碼
異常捕獲處理類
import com.demo1.domain.Result;
import com.demo1.utils.ResultUtil;

import com.demo1.error.BusinessException;
import org.slf4.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice//異常集中處理,更好的使業務邏輯與異常處理剝離開
public class ExceptionHandle {

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

    @ExceptionHandler(value = Exception.class)//統一處理指定型別異常,從而能夠減少程式碼重複率和複雜度
    @ResponseBody
    public Result handle(Exception e) {
        if (e instanceof BusinessException) {
            BusinessException BusinessException = (BusinessException) e;
            return ResultUtil.error(BusinessException.getCode(), BusinessException.getMessage());
        }else {
            logger.error("【系統異常】{}", e);
            return ResultUtil.error(-1, "未知錯誤");
        }
    }
}
複製程式碼
slf4j

這裡使用了slf4j提供的日誌介面,簡單介紹一下slf4j:

官方文件定義:Simple Logging Facade for Java(SLF4J)用作各種日誌框架(例如java.util.logging,logback,log4j)的簡單外觀或抽象,允許終端使用者在部署 時插入所需的日誌記錄框架。

slf4j只是一個日誌標準,並不是日誌系統的具體實現。理解這句話非常重要,slf4j只做兩件事情:

  • 提供日誌介面
  • 提供獲取具體日誌物件的方法

slf4j-simple、logback都是slf4j的具體實現,log4j並不直接實現slf4j,但是有專門的一層橋接slf4j-log4j12來實現slf4j。

門面模式

slf4j是門面模式的典型應用,再簡單介紹一下門面模式:

門面模式,隱藏系統的複雜性,並向客戶端提供了一個可以訪問系統的介面。這種型別的設計模式屬於結構性模式,門面模式是物件的結構模式。為子系統中的一組介面提供了一個統一的訪問介面,這個介面使得子系統更容易被訪問或者使用。 其核心為外部與一個子系統的通訊必須通過一個統一的外觀物件進行,使得子系統更易於使用。用一張圖來表示門面模式的結構為:

門面模式結構圖

門面模式的核心為Facade即門面物件,門面物件核心為幾個點:

  • 知道所有子角色的功能和責任
  • 將客戶端發來的請求委派到子系統中,沒有實際業務邏輯
  • 不參與子系統內業務邏輯的實現
@ControllerAdvice和@ExceptionHandler註解

官方文件上對@ControllerAdvice的說法是作為@component的專門化,允許通過類路徑掃描自動檢測實現類。

它通常用於定義應用於所有@Requestmapping方法的@ExceptionHandler、@InitBinder和@ModelAttribute。意思是啟動應用後,被 @ExceptionHandler、@InitBinder、@ModelAttribute 註解的方法,都會作用在被@RequestMapping 註解的方法上。

@ExceptionHandler 攔截異常,我們可以通過該註解實現自定義異常處理。其中,@ExceptionHandler 配置的 value 指定需要攔截的異常型別。

參考:

www.importnew.com/28494.html

www.cnblogs.com/shihaiming/…

慕課課程《Spring Boot進階之Web進階》

相關文章