自定义全局异常类-统一处理异常
在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;
}
}