自定义全局异常类@ControllerAdvice-统一处理异常

自定义全局异常类-统一处理异常

在Springboot或者Springcloud开发中为了统一处理业务异常,需要自定义全局异常处理类统一处理业务异常。在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping注解的方法中,本篇内容讲解一下使用@ControllerAdvice和@ExceptionHandler注解,自定义全局异常处理类,统一处理异常。

引入必要的坐标

 		<dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-core</artifactId>
            <version>1.4.26</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>1.5.21.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-core</artifactId>
            <version>1.2.1.RELEASE</version>
        </dependency>

 <!--spring cloud-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

@ExceptionHandler注解介绍

启动程序应用后,@ExceptionHandler注解都会作用在被@RequestMapping注解的方法上。

@ExceptionHandler 拦截了异常,我们可以通过该注解的value实现自定义异常拦截处理。其中,@ExceptionHandler 配置的 value 指定需要拦截的异常类型。

package com.mall.exception;

import com.netflix.hystrix.exception.HystrixBadRequestException;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import com.netflix.hystrix.exception.HystrixRuntimeException.FailureType;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.DefaultErrorAttributes;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
    public static final String PARAM_VALID_ERROR_CODE = "0-0012";
    public static final String PARAM_VALID_ERROR_MSG = "参数校验错误: ";
    private Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    @Autowired
    private DefaultErrorAttributes defaultErrorAttributes;
    @Autowired
    private HttpServletRequest request;
    @Autowired
    private Tracer tracer;
    @Value("${ecc.error.debug:false}")
    private Boolean ECC_ERROR_DEBUG = false;

    public GlobalExceptionHandler() {
    }

    private String timestampToDate(Object timestamp) {
        SimpleDateFormat d = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        Date result;
        try {
            if (timestamp instanceof Date) {
                result = (Date)timestamp;
                return d.format(result);
            }
        } catch (NumberFormatException var4) {
            this.LOG.error("时间戳转日期异常:error:{}", var4);
        }

        result = new Date();
        return d.format(result);
    }

    @ExceptionHandler({BaseException.class})
    public ResponseEntity<String> serviceException(BaseException exception) {
        return this.handleBaseException(exception);
    }

    @ExceptionHandler({Exception.class})
    public ResponseEntity<Object> systemException(Exception exception) {
        Map<String, Object> errorAttributes = this.errorAttributes();
        errorAttributes.put("msg", ResponseCode.EXCEPTION_CODE.getMsg());
        errorAttributes.put("code", ResponseCode.EXCEPTION_CODE.getCode());
        if (exception instanceof NullPointerException) {
            errorAttributes.put("message", "空指针异常");
        }

        this.LOG.error("系统异常信息,result:{},error:{}", errorAttributes, exception);
        return this.getResponseEntity(errorAttributes, HttpStatus.INTERNAL_SERVER_ERROR, exception);
    }

    private ResponseEntity getResponseEntity(Map<String, Object> errorAttributes, HttpStatus status, Exception ex) {
        this.sqlIgnoreMessage(errorAttributes);
        String traceId = this.tracer.getCurrentSpan().traceIdString();
        errorAttributes.put("traceId", traceId);
        this.tracer.addTag("error", ex.getMessage());
        this.tracer.close(this.tracer.getCurrentSpan());
        if (!this.ECC_ERROR_DEBUG) {
            errorAttributes.remove("trace");
            errorAttributes.remove("exception");
        }

        return new ResponseEntity(errorAttributes, status);
    }

    private void sqlIgnoreMessage(Map<String, Object> errorAttributes) {
        String exception = (String)errorAttributes.get("exception");
        if (StringUtils.containsIgnoreCase(exception, "jdbc") || StringUtils.containsIgnoreCase(exception, "sql")) {
            errorAttributes.put("message", "sql异常");
        }

    }

    @ExceptionHandler({HystrixRuntimeException.class, HystrixBadRequestException.class})
    public ResponseEntity hystrixException(Exception exception) {
        Map<String, Object> errorAttributes = new HashMap();
        if (exception instanceof HystrixRuntimeException) {
            if (exception.getCause() instanceof ServiceServerException) {
                ServiceServerException ex = (ServiceServerException)exception.getCause();
                errorAttributes = ex.getMap();
                ((Map)errorAttributes).put("msg", ResponseCode.REMOTE_CALL_SERVICE_CODE.getMsg());
                ((Map)errorAttributes).put("code", ResponseCode.REMOTE_CALL_SERVICE_CODE.getCode());
                this.LOG.error("远程调用服务端异常信息,result:{}", errorAttributes);
            } else {
                HystrixRuntimeException hystrixRuntimeException = (HystrixRuntimeException)exception;
                FailureType failureType = hystrixRuntimeException.getFailureType();
                String fallbackException = hystrixRuntimeException.getFallbackException().getClass().toString();
                ((Map)errorAttributes).put("error", failureType);
                ((Map)errorAttributes).put("exception", fallbackException);
                ((Map)errorAttributes).put("message", hystrixRuntimeException.getCause() != null ? hystrixRuntimeException.getCause().getMessage() : hystrixRuntimeException.getMessage());
                ((Map)errorAttributes).put("msg", ResponseCode.REMOTE_CALL_HYSTRIXRUNTIME_CODE.getMsg());
                ((Map)errorAttributes).put("code", ResponseCode.REMOTE_CALL_HYSTRIXRUNTIME_CODE.getCode());
                errorAttributes = this.addStackTrace((Map)errorAttributes, hystrixRuntimeException);
                this.LOG.error("远程调用运行时异常信息,result:{},error:{}", errorAttributes, hystrixRuntimeException);
            }
        } else if (exception instanceof ServiceClientException) {
            ServiceClientException serviceClientException = (ServiceClientException)exception;
            Map<String, Object> map = serviceClientException.getMap();
            errorAttributes = map;
            map.put("msg", ResponseCode.REMOTE_CALL_CLIENT_CODE.getMsg());
            map.put("code", ResponseCode.REMOTE_CALL_CLIENT_CODE.getCode());
            this.LOG.error("远程调用客户端异常信息,result:{},error:{}", map, serviceClientException);
        }

        ((Map)errorAttributes).put("timestamp", this.timestampToDate(new Date()));
        return this.getResponseEntity((Map)errorAttributes, HttpStatus.INTERNAL_SERVER_ERROR, exception);
    }

    private Map<String, Object> addStackTrace(Map<String, Object> errorAttributes, Throwable error) {
        StringWriter stackTrace = new StringWriter();
        error.printStackTrace(new PrintWriter(stackTrace));
        stackTrace.flush();
        errorAttributes.put("trace", stackTrace.toString());
        return errorAttributes;
    }

    private ResponseEntity handleBaseException(BaseException exception) {
        Map<String, Object> errorAttributes = this.errorAttributes();
        errorAttributes.put("msg", exception.getMsg());
        errorAttributes.put("code", exception.getCode());
        this.LOG.error("业务返回异常信息,result:{}", errorAttributes);
        return this.getResponseEntity(errorAttributes, HttpStatus.INTERNAL_SERVER_ERROR, exception);
    }

    @ExceptionHandler({ConstraintViolationException.class})
    public ResponseEntity violationException(ConstraintViolationException exception) {
        Map<String, Object> errorAttributes = this.errorAttributes();
        List result = new ArrayList();
        Set<ConstraintViolation<?>> errorList = exception.getConstraintViolations();
        errorList.forEach((error) -> {
            result.add(error.getMessage());
        });
        errorAttributes.put("message", result);
        StringBuilder sb = new StringBuilder("参数校验错误: ");
        errorList.forEach((error) -> {
            sb.append(error.getMessage()).append(" ");
        });
        errorAttributes.put("msg", sb.toString());
        errorAttributes.put("code", "0-0012");
        this.LOG.error("参数校验异常:result:{},error:{}", errorAttributes, exception.getConstraintViolations());
        return this.getResponseEntity(errorAttributes, HttpStatus.BAD_REQUEST, exception);
    }

    protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Map<String, Object> errorAttributes = this.errorAttributes();
        List fieldErrors;
        List argumentInvalidResults;
        if (ex instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException exception = (MethodArgumentNotValidException)ex;
            fieldErrors = exception.getBindingResult().getFieldErrors();
            argumentInvalidResults = this.getArgumentInvalidResults(fieldErrors);
            errorAttributes.put("msg", ResponseCode.NOT_VALID_ARGUMENT_CODE.getMsg());
            errorAttributes.put("code", ResponseCode.NOT_VALID_ARGUMENT_CODE.getCode());
            errorAttributes.put("message", argumentInvalidResults);
            this.LOG.error("参数校验异常:result:{},error:{}", errorAttributes, exception);
        } else if (ex instanceof BindException) {
            BindException exception = (BindException)ex;
            fieldErrors = exception.getBindingResult().getFieldErrors();
            argumentInvalidResults = this.getArgumentInvalidResults(fieldErrors);
            StringBuilder sb = new StringBuilder("参数校验错误: ");
            argumentInvalidResults.stream().forEach((argumentInvalidResult) -> {
                sb.append(argumentInvalidResult.getDefaultMessage()).append(" ");
            });
            errorAttributes.put("msg", sb.toString());
            errorAttributes.put("code", ResponseCode.NOT_VALID_ARGUMENT_CODE.getCode());
            errorAttributes.put("message", argumentInvalidResults);
            this.LOG.error("参数校验异常:result:{},error:{}", errorAttributes, exception);
        } else if (ex instanceof MethodArgumentTypeMismatchException) {
            MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException)ex;
            errorAttributes.put("msg", ResponseCode.PARAMETER_CONVERSION_ERROR_CODE.getMsg());
            errorAttributes.put("code", ResponseCode.PARAMETER_CONVERSION_ERROR_CODE.getCode());
            errorAttributes.put("error", String.format("异常参数:%s", exception.getName()));
            this.LOG.error("参数类型转换异常,方法:{},参数:{},信息:{},result:{},error:{}", new Object[]{exception.getParameter().getMethod().getName(), exception.getName(), exception.getLocalizedMessage(), errorAttributes, exception});
        } else if (ex instanceof MissingServletRequestParameterException) {
            MissingServletRequestParameterException exception = (MissingServletRequestParameterException)ex;
            errorAttributes.put("msg", ResponseCode.MISSING_REQUESTPA_RAMETER_CODE.getMsg());
            errorAttributes.put("code", ResponseCode.MISSING_REQUESTPA_RAMETER_CODE.getCode());
            errorAttributes.put("error", String.format("缺少参数:%s", exception.getParameterName()));
            this.LOG.error("缺少参数异常,result:{},error:{}", errorAttributes, exception);
        } else if (ex instanceof HttpMessageConversionException) {
            HttpMessageConversionException exception = (HttpMessageConversionException)ex;
            errorAttributes.put("msg", ResponseCode.SPRING_MVC_HTTPMESSAGE_CONVERSION_CODE.getMsg());
            errorAttributes.put("code", ResponseCode.SPRING_MVC_HTTPMESSAGE_CONVERSION_CODE.getCode());
            this.LOG.error("spring mvc消息转换器 参数序列化异常异常,result:{},error:{}", errorAttributes, exception);
        }

        return this.getResponseEntity(errorAttributes, status, ex);
    }

    private List<ArgumentInvalidResult> getArgumentInvalidResults(List<FieldError> exception) {
        List<ArgumentInvalidResult> invalidArguments = new ArrayList();
        Iterator var3 = exception.iterator();

        while(var3.hasNext()) {
            FieldError error = (FieldError)var3.next();
            ArgumentInvalidResult invalidArgument = new ArgumentInvalidResult();
            invalidArgument.setDefaultMessage(error.getDefaultMessage());
            invalidArgument.setField(error.getField());
            invalidArgument.setRejectedValue(error.getRejectedValue());
            invalidArguments.add(invalidArgument);
        }

        if (invalidArguments.size() == 0) {
            ArgumentInvalidResult argumentInvalidResult = new ArgumentInvalidResult();
            argumentInvalidResult.setDefaultMessage("无错误信息");
            invalidArguments.add(argumentInvalidResult);
        }

        return invalidArguments;
    }

    private Map<String, Object> errorAttributes() {
        Boolean debug = true;
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        Map<String, Object> errorAttributes = this.defaultErrorAttributes.getErrorAttributes(requestAttributes, debug);
        errorAttributes.put("timestamp", this.timestampToDate(errorAttributes.get("timestamp")));
        errorAttributes.remove("status");
        errorAttributes.remove("errors");
        return errorAttributes;
    }
}
package com.mall.exception;

public class ArgumentInvalidResult {
    private String field;
    private Object rejectedValue;
    private String defaultMessage;

    public ArgumentInvalidResult() {
    }
    
	// set...  get...  toString...
}

package com.mall.exception;

public class BaseException extends RuntimeException {
    protected String code;
    protected String msg;

    public BaseException() {
    }

    public BaseException(String code, String message) {
        super(message);
        this.code = code;
        this.msg = message;
    }

    public BaseException(String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    // set...  get...  toString...
}

package com.mall.exception;

import com.netflix.hystrix.exception.HystrixBadRequestException;
import java.util.Map;

public class ServiceClientException extends HystrixBadRequestException {
    private String code;
    private String msg;
    private Map<String, Object> map;

    public ServiceClientException(Map<String, Object> map) {
        super(map.get("message").toString() + "<<==========>>" + map.get("msg") == null ? ResponseCode.UNKNOWN_CODE.getMsg() : map.get("msg").toString());
        this.code = map.get("code") == null ? ResponseCode.UNKNOWN_CODE.getCode() : map.get("code").toString();
        this.msg = map.get("msg") == null ? ResponseCode.UNKNOWN_CODE.getMsg() : map.get("msg").toString();
        this.map = map;
    }

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

    public ServiceClientException(String message, Throwable cause) {
        super(message, cause);
    }

    // set...  get...  
}

package com.mall.exception;

import java.util.Map;

public class ServiceServerException extends BaseException {
    private Map<String, Object> map;

    public ServiceServerException(Map<String, Object> map) {
        super(map.get("code") == null ? ResponseCode.UNKNOWN_CODE.getCode() : map.get("code").toString(), map.get("msg") == null ? ResponseCode.UNKNOWN_CODE.getMsg() : map.get("msg").toString());
        map.put("remoteCode", map.get("code") == null ? ResponseCode.UNKNOWN_CODE.getCode() : map.get("code").toString());
        map.put("remoteMsg", map.get("msg") == null ? ResponseCode.UNKNOWN_CODE.getMsg() : map.get("msg").toString());
        this.map = map;
    }

    public Map<String, Object> getMap() {
        return this.map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
}

package com.exception;

public enum ResponseCode {
    EXCEPTION_CODE("99999-0", "系统异常,请稍后重试"),
    REMOTE_CALL_SERVICE_CODE("99999-1", "远程调用服务端异常"),
    NOT_VALID_ARGUMENT_CODE("99999-2", "参数校验异常"),
    PARAMETER_CONVERSION_ERROR_CODE("99999-3", "参数类型转换异常"),
    MISSING_REQUESTPA_RAMETER_CODE("99999-4", "缺少参数异常"),
    REMOTE_CALL_CLIENT_CODE("99999-5", "远程调用客户端异常"),
    REMOTE_CALL_HYSTRIXRUNTIME_CODE("99999-6", "远程调用运行时异常"),
    SPRING_MVC_HTTPMESSAGE_CONVERSION_CODE("99999-7", "参数序列化异常"),
    UNKNOWN_CODE("99999-100", "未知的异常");

    private String code;
    private String msg;

    private ResponseCode(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public String getCode() {
        return this.code;
    }

    public String getMsg() {
        return this.msg;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

曙光][照亮黑夜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值