记录一下认为相当不错的一种spring boot的统一异常处理方案。
定义异常类:
public class ErrorBody implements Serializable {
private static final long serialVersionUID = -6005313381413512238L;
/**
* 错误码
*/
private String code;
/**
* 错误提示信息
*/
private String msg;
/**
* 异常发生的时间
*/
private Date timestamp;
/**
* 二级错误码
*/
private String sub_code;
/**
* 二级错误消息
*/
private String sub_msg;
/**
* 错误调用的URI资源
*/
private String path;
/**
* 请求标识
*/
private String request_id;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Date getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
this.timestamp = timestamp;
}
public String getSub_code() {
return sub_code;
}
public void setSub_code(String sub_code) {
this.sub_code = sub_code;
}
public String getSub_msg() {
return sub_msg;
}
public void setSub_msg(String sub_msg) {
this.sub_msg = sub_msg;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getRequest_id() {
return request_id;
}
public void setRequest_id(String request_id) {
this.request_id = request_id;
}
}
统一异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* @param request
* @param exception
* @return ErrorBody
* @throws
*/
@ExceptionHandler(value = RuntimeException.class)
public ResponseEntity<ErrorBody> errorHandlerOverJson(HttpServletRequest request,HttpServletResponse resp,
RuntimeException exception) {
ResponseStatus status = AnnotationUtils.findAnnotation(exception.getClass(), ResponseStatus.class);
ErrorCode errorCode = AnnotationUtils.findAnnotation(exception.getClass(), ErrorCode.class);
ErrorBody body = new ErrorBody();
body.setCode(ErrorType.Remote_server_error.getCode());
body.setMsg(ErrorType.Remote_server_error.getCnMsg());
//检查异常code
if(exception instanceof BusinessException){
String code = ((BusinessException)exception).getCode();
String msg = ((BusinessException)exception).getMessage();
if(StringUtils.hasText(code)){
body.setSub_code(code);
}
if(StringUtils.hasText(msg)){
body.setSub_msg(msg);
}
}
//异常code没定义,检查注解
if(body.getSub_code() == null){
Optional.ofNullable(errorCode).ifPresent(e->{
if(!StringUtils.hasText(body.getSub_code())){
body.setSub_code(e.value());
}
if(!StringUtils.hasText(body.getSub_msg())){
body.setSub_msg(e.msg());
}
if(e.parent() != null){
body.setCode(e.parent().getCode());
body.setMsg(e.parent().getCnMsg());
}
});
}
body.setPath(request.getRequestURI());
body.setTimestamp(new Date());
return new ResponseEntity<ErrorBody>(body, status==null?HttpStatus.INTERNAL_SERVER_ERROR:status.code());
}
异常基类:
public class BusinessException extends RuntimeException {
private static final long serialVersionUID = 7972934648727587910L;
private String code;
public BusinessException() {
}
public BusinessException(String message) {
super(message);
}
public BusinessException(String code, String message) {
super(message);
this.code = code;
}
public BusinessException(Throwable throwable) {
super(throwable);
}
public BusinessException(Throwable throwable, String message) {
super(message, throwable);
}
public BusinessException(String code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
public String getCode() {
return this.code;
}
public void setCode(String code) {
this.code = code;
}
}
基础异常类型:(可以定义很多一级异常类型,其他业务子系统的异常须归类到这个里面,如果没有明确指定将默认归类到Remote_server_error[
convention over configuration]
)
public enum ErrorType {
Insufficient_permissions("11", "权限错误"),
Invalid_parameter("12", "参数错误"),
Resource_not_found("40", "请求资源不存在"),
Remote_server_error("50", "服务器异常");
private String code;
/**
* 错误消息(中文)
*/
private String cnMsg;
private ErrorType(String code, String cnMsg){
this.code = code;
this.cnMsg = cnMsg;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getCnMsg() {
return cnMsg;
}
public void setCnMsg(String cnMsg) {
this.cnMsg = cnMsg;
}
}
ErroCode注解
@Target({ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ErrorCode {
String value() default "";
String msg() default "";
/**
* 声明异常所属大类,默认是ErrorType.Remote_server_error
* @return ErrorType
* @throws
*/
ErrorType parent() default ErrorType.Remote_server_error;
}
子业务系统自定义子异常:
@ResponseStatus(HttpStatus.NOT_FOUND)
@ErrorCode(value="task.not_found", msg="任务不存在.", parent=ErrorType.Resource_not_found)
public class TaskNotFoundException extends BusinessException{
private static final long serialVersionUID = 5307374358980657246L;
public TaskNotFoundException() {
super();
}
public TaskNotFoundException(String message) {
super(message);
}
public TaskNotFoundException(String code, String message) {
super(code, message);
}
public TaskNotFoundException(String message, Throwable e) {
super(null, message, e);
}
public TaskNotFoundException(String code, String message, Throwable cause) {
super(code, message, cause);
}
public TaskNotFoundException(Throwable cause) {
super(cause);
}
}
测试:
@RequestMapping(value = "/tasks/{taskCode}/exec", method = RequestMethod.POST)
public String executeImmediatly(@PathVariable("taskCode") @NotNull String taskCode){
TaskQueryDto query = new TaskQueryDto();
query.setTaskCode(taskCode);
List<TaskInfo> tasks = taskDao.findAll(query);
if(CollectionUtils.isEmpty(tasks)){
throw new TaskNotFoundException("任务不存在:"+taskCode);
}
TaskEvent event = new TaskEvent();
event.setFlowNo(UUID.randomUUID().toString());
event.setTaskCode(taskCode);
event.setTask(BeanCopyUtil.copyProperties(tasks.get(0), TaskDto.class));
tracker.assignTask(event);
return event.getFlowNo();
}
返回:
{
"code": "40",
"msg": "请求资源不存在",
"timestamp": 1503110838168,
"sub_code": "task.not_found",
"sub_msg": "任务不存在:eeee2",
"path": "/task/v1/tasks/eeee2/exec",
"request_id": null
}