Spring 兩種全局異常比較---泛型,建造者設(shè)計(jì)模式

把我兩次書(shū)寫(xiě)全局異常捕獲統(tǒng)一處理返回JSON的經(jīng)驗(yàn)分享給大家。其中真的還是有點(diǎn)小進(jìn)步的。里面涉及到泛型和建造者的使用,還是能學(xué)到挺多的。

實(shí)現(xiàn)的思路是

  1. 全局異常的抓取。
  2. 封裝統(tǒng)一返回結(jié)果對(duì)象

第一種方式:我記得好像是參考阿里巴巴的黃勇同志寫(xiě)出來(lái)的。其實(shí)很簡(jiǎn)單通過(guò)ControllerAdvice做一Controller的AOP。然后通過(guò)攔截對(duì)應(yīng)的Exception可以自定義返回對(duì)應(yīng)的HttpStatus。然后封裝Reponse統(tǒng)一返回?cái)?shù)據(jù)即可。甚至連正常返回都可以封裝為一個(gè)Exception返回,可以參照下面的StatusSuccess,具體實(shí)現(xiàn)比較簡(jiǎn)單。

別人親測(cè),這里有一個(gè)天大的坑,入門(mén)的小伙伴要注意了。事務(wù)回滾的異常處理是需要extend RunTimeException 而繼承Exception 是沒(méi)辦法做到事務(wù)回滾的。

第二種方式:是我參考簡(jiǎn)書(shū)小伙伴的文章寫(xiě)出來(lái)的。另外加入了建造者設(shè)計(jì)模式,個(gè)人見(jiàn)解使用簡(jiǎn)單,代碼可讀性更好。實(shí)現(xiàn)思路更第一種類(lèi)似,但是在代碼上有了比較大的改動(dòng)。尤其是泛型的使用,和設(shè)計(jì)模式的使用。

兩種方式的比較:

  1. 弱點(diǎn)一:Controller返回的都是Object 。雖然可以滿(mǎn)足大部分的業(yè)務(wù),但是過(guò)了兩個(gè)月,前端問(wèn)你這個(gè)接口返回的是什么東西。多半你要找到你的dao層才能判斷了。而第二種方式則可以避免這種問(wèn)題。使用了泛型。很容易讓你清楚的知道data里面放到是什么

  2. 弱點(diǎn)二:代碼可讀性更強(qiáng)了。不會(huì)在Service里面throw new StatusSuccess()了。這段代碼始終讓我感覺(jué)奇奇怪怪的又說(shuō)不是是啥。
    第二種實(shí)現(xiàn)方式,則是在Controller里面使用Builder設(shè)計(jì)模式,讓代碼更加優(yōu)雅帥氣,MVC分層明顯,代碼可讀性強(qiáng),真的是一個(gè)很棒的實(shí)現(xiàn)方式。

第二種實(shí)現(xiàn)方式,直接上代碼了。

1 在controller 的實(shí)現(xiàn)效果。代碼簡(jiǎn)潔??勺x性??。

@RestController
@RequestMapping("/app/user")
public class ApiController {

    @RequestMapping("data")
    public RestResult<Object> text() throws Exception {
        throw new Exception();
    }

    @RequestMapping("getData")
    public RestResult<String> getData() throws Exception {
        return RestResultGenrator.build(
                xxxService.getData();
        );
    }
}

2 統(tǒng)一異常抓取。注意看我的RestResultBuild的構(gòu)建??雌饋?lái)更加美觀。


/**
 * RestExceptionHandler
 *
 * @author zf
 * @date 9/23/16
 */
@ControllerAdvice(annotations = RestController.class)
public class RestExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(RestExceptionHandler.class);

    @ExceptionHandler
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    private <T> RestResult<T> runtimeExceptionHandler(Exception e){
        LOGGER.error("------->error !" ,e);
        return new RestResultBuilder<T>()
                .setErrorCode(ErrorCode.ERROR)
                .setMessage(ErrorCode.ErrorMessage.ERROR)
                .build();
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    private <T> RestResult<T> illegalParamsExceptionHandler(MethodArgumentNotValidException e) {
        LOGGER.error("---------> invalid request!", e);
        return new RestResultBuilder<T>()
                .setErrorCode(ErrorCode.ERROR_METHOD)
                .setMessage(ErrorCode.ErrorMessage.ERROR_METHOD)
                .build();
    }


    @ExceptionHandler(ExpireException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    private <T> RestResult<T> expireHandler(MethodArgumentNotValidException e) {
        LOGGER.error("---------> invalid expire!", e);
        return new RestResultBuilder<T>()
                .setErrorCode(ErrorCode.EXPIRED)
                .setMessage(ErrorCode.ErrorMessage.EXPIRED)
                .build();
    }

}

3 使用統(tǒng)一泛型處理,增強(qiáng)Controller的可讀性。

/**
 * RestResult
 *
 * @author zf
 * @date 9/23/16
 */
public class RestResult <T>{
    private int errorCode;
    private String message;
    private T data;

    private RestResult(){}

    protected RestResult(int errorCode, String message, T data) {
        this.errorCode = errorCode;
        this.message = message;
        this.data = data;
    }

    public int getErrorCode() {
        return errorCode;
    }

    public RestResult setErrorCode(int errorCode) {
        this.errorCode = errorCode;
        return this;
    }

    public String getMessage() {
        return message;
    }

    public RestResult<T> setMessage(String message) {
        this.message = message;
        return this;
    }

    public T getData() {
        return data;
    }

    public RestResult<T> setData(T data) {
        this.data = data;
        return this;
    }
}

4 使用builder設(shè)計(jì)模式構(gòu)建統(tǒng)一返回對(duì)象。

/**
 * RestResultBuilder
 *
 * @author zf
 * @date 9/23/16
 */
public class RestResultBuilder<T> {

    private int errorCode = ErrorCode.VALID;

    private String message =ErrorCode.ErrorMessage.VALID;;

    private T data ;

    protected RestResultBuilder<T> setErrorCode(int errorCode){
        this.errorCode = errorCode;
        return this;
    }

    public RestResultBuilder<T> setMessage(String message){
        this.message = message;
        return this;
    }

    public RestResultBuilder<T> setData(T data){
        this.data = data;
        return this;
    }

    public RestResult<T> build(){
        return new RestResult<T>(errorCode,message,data);
    }


}

5 我是為了不想寫(xiě)new三個(gè)字母,所以寫(xiě)了第五個(gè)類(lèi),Genrator。

public class RestResultGenrator {

    public static <T> RestResult<T> build(){
        return build(null);
    }

    public static <T> RestResult<T> build(T t){
        return new RestResultBuilder<T>().setData(t).build();
    }
}

下面是第一種實(shí)現(xiàn)方式。

  1. 全局異常抓取類(lèi)
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
    Logger logger = Logger.getGlobal();

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Response handleHttpMessageNotReadableException(
            HttpMessageNotReadableException e) {
        logger.info("參數(shù)解析失敗" + e);
        return new Response().failure("could_not_read_json");
    }

    /**
     * 405 - Method Not Allowed
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Response handleHttpRequestMethodNotSupportedException(
            HttpRequestMethodNotSupportedException e) {
        logger.info("不支持當(dāng)前請(qǐng)求方法" + e);
        return new Response().failure("request_method_not_supported");
    }

    /**
     * 415 - Unsupported Media Type
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public Response handleHttpMediaTypeNotSupportedException(Exception e) {
        logger.info("不支持當(dāng)前媒體類(lèi)型" + e);
        return new Response().failure("content_type_not_supported");
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(Exception.class)
    public Response handleException(Exception e) {
        e.printStackTrace();
        return new Response().failure(e.getMessage());
    }

    /**
     * - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(StatusSuccess.class)
    public Response handleSuccessStatus(StatusSuccess e) {
        logger.info("StatusSuccess");
        return new Response(Response.SUCCESS_CODE, e.getMessage(), e.getData());
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ExpireException.class)
    public Response handleExipreStatus(Exception e) {
        logger.info("內(nèi)部錯(cuò)誤");
        return new Response().timeOut(e.getMessage());
    }

    /**
     * 500 - 內(nèi)部錯(cuò)誤
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(IllegalArgumentException.class)
    public Response parameterException(IllegalArgumentException e) {
        logger.info("非法參數(shù)" + e.toString());
        return new Response().failure(e.getMessage());
    }

    /**
     * 500 - Internal Server Error
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ShopStatusException.class)
    public Response errorShopStatus(Exception e) {
        logger.info("店鋪狀態(tài)異常");
        return new Response().shopStatusException(e.getMessage());
    }

    /**
     * 訂單狀態(tài)異常攔截器
     * 
     * @author yangyanchao
     * @date 2016年8月16日
     * @param e
     * @return
     */
    public Response orderStatusException(Exception e) {
        return new Response().shopStatusException(e.getMessage());
    }

}
  1. 統(tǒng)一返回實(shí)體類(lèi)
/**
 * Response
 * 
 * @author zf
 * @date 16/3/21
 */
public class Response {

    private static final String OK = "success";
        private static final String ERROR = "failure";
    private static final String TIMEOUT = "expired";

    public static final int SUCCESS_CODE = 1;
    public static final int FAIL_CODE = 0;
    public static final int TOKEN_INVALID = -1;
    public static final int SHOP_ABNORMAL = -2;
    

    @JsonView(BaseView.BaseResponse.class)
    private int status;

    @JsonView(BaseView.BaseResponse.class)
    private String msg;

    @JsonView(BaseView.BaseResponse.class)
    private Object data;

    public Response success() {
        this.status = SUCCESS_CODE;
        this.msg = OK;
        return this;
    }

    public Response success(Object data) {
        this.status = SUCCESS_CODE;
        this.msg = OK;
        this.data = data;
        return this;
    }

    public Response failure() {
        this.status = FAIL_CODE;
        this.msg = ERROR;
        return this;
    }

    public Response() {
    }

    public Response(int status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public Response expireException(String msg) {
        this.status = TOKEN_INVALID;
        this.msg = msg;
        return this;
    }

    public Response timeOut(String msg) {
        this.status = TOKEN_INVALID;
        this.msg = msg;
        return this;
    }
    
    public Response shopStatusException(String msg) {
        this.status = SHOP_ABNORMAL;
        this.msg = msg;
        return this;
    }

    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

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

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

    public Response failure(String message) {
        this.status = FAIL_CODE;
        this.msg = message;
        return this;
    }

    public Object getData() {
        return data;
    }

}

3.service 使用例子

   /**
     * 保存用戶(hù)
     *
     * @param principle
     *            用戶(hù)信息
     * @param uadId
     *            用戶(hù)地址id
     * @return 保存
     */
    public void save(Principle principle, Integer uadId) throws NotSamePeopleException, StatusSuccess {
        AsUserAddress userAddress = asUserAddressMapper.selectByPrimaryKey(uadId);
        if (userAddress.getUserId().equals(principle.getUserId())) {
            principle.setAddressId(uadId);
            AsUser u = new AsUser();
            u.setUserId(principle.getUserId());
            u.setAddressId(uadId);
            asUsersMapper.updateByPrimaryKeySelective(u);
            throw new StatusSuccess();
        } else {
            throw new NotSamePeopleException();
        }

    }

3 . 正確返回的異常處理,注意看這里是封裝了data 的。

/**
 * OrderStatusException
 *
 * @author zf
 * @date 16/7/14
 */
public class StatusSuccess extends Exception{
    private static String msg = "操作成功";
    private Object data;
    public StatusSuccess(){
        super(msg);
    }

    public StatusSuccess(String msg) {
        super(msg);
    }

    public StatusSuccess(Object data) {
        super(msg);
        this.data = data;
    }

    public StatusSuccess(String message, Object data) {
        super(message);
        this.data = data;
    }

    public static String getMsg() {
        return msg;
    }

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

    public Object getData() {
        return data;
    }

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

最后用postMan得出最終相同的結(jié)果。

Paste_Image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,551評(píng)論 19 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,007評(píng)論 25 709
  • 2017-08-21 分享經(jīng)濟(jì) 人體是一個(gè)有機(jī)整體,但每個(gè)零件走向衰老的過(guò)程卻有先后?!袄狭死狭恕保簧俣鄽q的...
    熊俊容閱讀 562評(píng)論 0 0
  • 今天是我連續(xù)晨讀感悟的第24天,哈哈哈,真沒(méi)有想到,自己竟然堅(jiān)持下來(lái)了。 這期間有那么幾天特別想放棄,但是一想到堅(jiān)...
    鮮嫩多汁小肉包閱讀 211評(píng)論 2 3

友情鏈接更多精彩內(nèi)容