项目中设计一个简洁优雅高效的全局异常处理(只需增加两个类)

你们有没有见过一个项目整个controller层每一个方法写一个try..catch来处理异常情况的,例如下面这种:

不但如此,在业务层的操作也全都是 try...catch,异常全靠打印异常堆栈;这是我待过的第一家公司的代码。

后来其实一直想重构一下,做一个全局的异常处理,但是害怕一改全是问题,心生恐惧,犹如下图(直到离职都没敢动手):

下面写一个简洁优雅的全局异常捕获处理的方法:

 需要用到两个注解:

  • @ControllerAdvice:是Spring3.2提供的新注解,它是一个Controller增强器,可对controller中被 @RequestMapping注解的方法加一些逻辑处理。最常用的就是异常处理
  • @ExceptionHandler: 异常处理,Exception的匹配 符合" 就近原则 ", 一次声明,全接口生效

实现

一、首先添加一个业务异常类 ServiceException.java 继承自RuntimeException,用于项目中业务异常。

package com.pn.ebs.util;

public class ServiceException extends RuntimeException {

    public ServiceException(String message) {
        super(message);
    }
}

说明一下:

  • 如果希望写一个检查性异常类,则需要继承 Exception 类。
  • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

这里我是需要写一个运行时异常,所以继承自 RuntimeException

二、写一个 ExceptionTranslator.java ,包含两个方法,errorHandler和handleBusinessException ,一个处理全局(未知)异常 ,一个处理业务异常。

package com.pn.ebs.util;

import com.pn.ebs.dto.BaseResp;
import org.slf4j.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 ExceptionTranslator {
    private final Logger log = LoggerFactory.getLogger(ExceptionTranslator.class);

    // 全局异常
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public BaseResp errorHandler(Exception ex) {
        log.error(ex.getLocalizedMessage(), ex);
        return BaseResp.getFailInstance();
    }

    // 业务异常
    @ResponseBody
    @ExceptionHandler(value = ServiceException.class)
    public BaseResp handleBusinessException(ServiceException ex) {
        log.error(ex.getLocalizedMessage(), ex);
        return BaseResp.getFailInstance(ex.getMessage());
    }
}

其中BaseResp.java 是定义的一个返回结果类型类。

package com.pn.ebs.dto;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;

import java.io.Serializable;

//保证序列化json的时候,如果是null的对象,key也会消失
@Data
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY, getterVisibility=JsonAutoDetect.Visibility.NONE)
public class BaseResp implements Serializable {

    public final static Integer HTTP_OK = 200;
    public final static String MESSAGE_OK = "SUCCESS";

    public final static Integer HTTP_ERROR_500 = 500;
    public final static String MESSAGE_ERROR = "The system error, please wait.";

    public final static Integer HTTP_ERROR_501 = 501;

    @JSONField(name = "retCode")
    private Integer retCode;

    @JSONField(name = "message")
    private String message;

    @JSONField(name = "data")
    private Object data;

    public BaseResp() { }

    public BaseResp(int retCode, String message, Object data) {
        this.retCode = retCode;
        this.message = message;
        this.data = data;
    }

    public static BaseResp getSuccessInstance() {
        return new BaseResp(HTTP_OK, MESSAGE_OK, null);
    }

    public static BaseResp getSuccessInstance(Object data) {
        return new BaseResp(HTTP_OK, MESSAGE_OK, data);
    }

    // 系统异常
    public static BaseResp getFailInstance() {
        return new BaseResp(HTTP_ERROR_500, MESSAGE_ERROR, null);
    }

    //  业务异常
    public static BaseResp getFailInstance(String errorMsg) {
        return new BaseResp(HTTP_ERROR_501, errorMsg, null);
    }

    public Integer getRetCode() {
        return retCode;
    }

    public void setRetCode(Integer retCode) {
        this.retCode = retCode;
    }

    public String getMessage() {
        return message;
    }

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

    public Object getData() {
        return data;
    }

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

}

三、使用

  • 全局异常:

在controller层写一个Get方法如下:

    @GetMapping("/dispatcher/test")
    public BaseResp test() {
        int s = 3 / 0;
        return BaseResp.getSuccessInstance();
    }

当调用此接口时肯定会抛出异常,而不会运行到test中最后一行 return BaseResp.getSuccessInstance(); 这一行。此异常 会被ExceptionTranslator 的errorHandler方法捕获,然后 log.error(ex.getLocalizedMessage(), ex); 打印错误日志,最后return BaseResp.getFailInstance(); 返回 错误信息 500, The system error, please wait.  可以打断电调试验证一下过程。

控制台日志:

  • 业务异常

改造一下刚刚的test方法,throw 一个异常 ServiceException。

    @GetMapping("/dispatcher/test")
    public BaseResp test() {
//        int s = 3 / 0;
        if (true) {
            throw new ServiceException("catch example2 exception");
        }
        return BaseResp.getSuccessInstance();
    }

当接口被调用时,会抛出一个 ServiceException,然后被ExceptionTranslator类中  handleBusinessException 方法捕获,然后执行log.error(ex.getLocalizedMessage(), ex); 输出错误日志,之后执行 return BaseResp.getFailInstance(ex.getMessage());返回错误信息:

控制台:

这样 ,每个接口方法的异常都会被 errorHandler 捕获,不需要 在每个接口方法中处理异常了,而业务上已知的异常直接throw new ServiceExption(msg) 就可以了。是不是简洁了不少。

发布了49 篇原创文章 · 获赞 19 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/shenju2011/article/details/103801424