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

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

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

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

第一種方式:我記得好像是參考阿里巴巴的黃勇同志寫出來的。其實(shí)很簡(jiǎn)單通過ControllerAdvice做一Controller的AOP卜范。然后通過攔截對(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è)天大的坑,入門的小伙伴要注意了怀薛。事務(wù)回滾的異常處理是需要extend RunTimeException 而繼承Exception 是沒辦法做到事務(wù)回滾的刺彩。

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

兩種方式的比較:

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

  2. 弱點(diǎn)二:代碼可讀性更強(qiáng)了。不會(huì)在Service里面throw new StatusSuccess()了寝殴。這段代碼始終讓我感覺奇奇怪怪的又說不是是啥蒿叠。
    第二種實(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)潔鬓照。可讀性??孤紧。

@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)建『畔裕看起來更加美觀臭猜。


/**
 * 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 我是為了不想寫new三個(gè)字母,所以寫了第五個(gè)類揽碘,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. 全局異常抓取類
@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)前媒體類型" + 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í)體類
/**
 * 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 使用例子

   /**
     * 保存用戶
     *
     * @param principle
     *            用戶信息
     * @param uadId
     *            用戶地址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)系作者
  • 序言:七十年代末掖桦,一起剝皮案震驚了整個(gè)濱河市本昏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌枪汪,老刑警劉巖凛俱,帶你破解...
    沈念sama閱讀 222,729評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異料饥,居然都是意外死亡蒲犬,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門岸啡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來原叮,“玉大人,你說我怎么就攤上這事巡蘸》芰ィ” “怎么了?”我有些...
    開封第一講書人閱讀 169,461評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵悦荒,是天一觀的道長(zhǎng)唯欣。 經(jīng)常有香客問我,道長(zhǎng)搬味,這世上最難降的妖魔是什么境氢? 我笑而不...
    開封第一講書人閱讀 60,135評(píng)論 1 300
  • 正文 為了忘掉前任蟀拷,我火速辦了婚禮,結(jié)果婚禮上萍聊,老公的妹妹穿的比我還像新娘问芬。我一直安慰自己,他們只是感情好寿桨,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評(píng)論 6 398
  • 文/花漫 我一把揭開白布此衅。 她就那樣靜靜地躺著,像睡著了一般亭螟。 火紅的嫁衣襯著肌膚如雪挡鞍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,736評(píng)論 1 312
  • 那天预烙,我揣著相機(jī)與錄音匕累,去河邊找鬼。 笑死默伍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的衰琐。 我是一名探鬼主播也糊,決...
    沈念sama閱讀 41,179評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼羡宙!你這毒婦竟也來了狸剃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,124評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤狗热,失蹤者是張志新(化名)和其女友劉穎钞馁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體匿刮,經(jīng)...
    沈念sama閱讀 46,657評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡僧凰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了熟丸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片训措。...
    茶點(diǎn)故事閱讀 40,872評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖光羞,靈堂內(nèi)的尸體忽然破棺而出绩鸣,到底是詐尸還是另有隱情,我是刑警寧澤纱兑,帶...
    沈念sama閱讀 36,533評(píng)論 5 351
  • 正文 年R本政府宣布呀闻,位于F島的核電站,受9級(jí)特大地震影響潜慎,放射性物質(zhì)發(fā)生泄漏捡多。R本人自食惡果不足惜蓖康,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望局服。 院中可真熱鬧钓瞭,春花似錦、人聲如沸淫奔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唆迁。三九已至鸭丛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間唐责,已是汗流浹背鳞溉。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鼠哥,地道東北人熟菲。 一個(gè)月前我還...
    沈念sama閱讀 49,304評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像朴恳,于是被迫代替她去往敵國(guó)和親抄罕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容