对于一些我们自定义的一些异常,或者一些其他的异常信息,我们不希望页面看到一大串bug信息,想友好的提示给用户,这样的需要可以利用的spring的全局异常处理机制实现,spring提供了多种异常处理的方式,下面这个方式只是其中一个,我们项目中是这么用的,参考一下我们项目中的实现,做一次记录。
先认识一个类ResponseEntityExceptionHandler
这个类里面封装了各种可能出现的异常,ResponseEntityExceptionHandler源码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.web.servlet.mvc.method.annotation;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
public abstract class ResponseEntityExceptionHandler {
public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
protected static final Log pageNotFoundLogger = LogFactory.getLog("org.springframework.web.servlet.PageNotFound");
protected final Log logger = LogFactory.getLog(this.getClass());
public ResponseEntityExceptionHandler() {
}
@ExceptionHandler({NoSuchRequestHandlingMethodException.class, HttpRequestMethodNotSupportedException.class, HttpMediaTypeNotSupportedException.class, HttpMediaTypeNotAcceptableException.class, MissingPathVariableException.class, MissingServletRequestParameterException.class, ServletRequestBindingException.class, ConversionNotSupportedException.class, TypeMismatchException.class, HttpMessageNotReadableException.class, HttpMessageNotWritableException.class, MethodArgumentNotValidException.class, MissingServletRequestPartException.class, BindException.class, NoHandlerFoundException.class, AsyncRequestTimeoutException.class})
public final ResponseEntity
处理结果都是封装成一个ResponseEntity对象。通过ResponseEntity我们可以指定需要响应的状态码、header和body等信息,响应的body会被HttpMessageConverter处理,所以如果你响应的body是一个对象,而你的HttpMessageConverter列表中有一个是可以把对象转换为JSON的HttpMessageConverter,那么客户端收到的就是一段JSON。ResponseEntityExceptionHandler是一个抽象类,通常我们需要定义一个用来处理异常的使用@ControllerAdvice
注解标注的异常处理类来继承自ResponseEntityExceptionHandler。
写一个类使用@ControllerAdvice注解标记,
继承ResponseEntityExceptionHandler
package com.soecode.lyf.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
import javax.servlet.http.HttpServletRequest;
/**
* @Description
* @Author DJZ-WWS
* @Date 2019/6/11 20:00
*/
@ControllerAdvice
public class BaseGlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(value = Exception.class)
@ResponseBody
public ResponseResult exceptionHandler(HttpServletRequest req, Exception e) {
if (EmptyUtils.isNotEmpty(e.getMessage())&&e.getMessage().indexOf("提示") != -1) {
return ResponseResult.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
}
return ResponseResult.fail(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务内部错误");
}
@ExceptionHandler(value = ParamInvalidException.class)
@ResponseBody
public ResponseResult paramInvalidExceptionHandler(HttpServletRequest req, ParamInvalidException e) throws Exception {
return ResponseResult.fail(HttpStatus.BAD_REQUEST.value(), e.getMessage());
}
}
返回的结果会被解析成json格式,这个类是我们项目中的,这里直接拿来用了。
package com.soecode.lyf.exception;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
@Data
@Builder
@ApiModel(value = "ResponseResult", discriminator = "ͨ通用返回信息", subTypes = {ResponseResult.class})
public class ResponseResult {
@ApiModelProperty("状态码")
private int code;
@ApiModelProperty("返回描述")
private String msg;
@ApiModelProperty("返回对象")
private T result;
private static final Integer HTTP_OK = 200;
private static final Integer HTTP_INTERNAL_SERVER_ERROR = 500;
private static final Integer HTTP_REQUEST_TIMEOUT = 408;
public static ResponseResult success() {
return new ResponseResultBuilder().code(HTTP_OK).msg("success").build();
}
public static ResponseResult success(T result) {
return new ResponseResultBuilder().code(HTTP_OK).msg("success").result(result).build();
}
public static ResponseResult success(T result, String msg) {
return new ResponseResultBuilder().code(HTTP_OK).msg(msg).result(result).build();
}
public static ResponseResult fail() {
return new ResponseResultBuilder().code(HTTP_INTERNAL_SERVER_ERROR).build();
}
public static ResponseResult fail(Exception e) {
return new ResponseResultBuilder().code(HTTP_INTERNAL_SERVER_ERROR).msg(e.getMessage().toString()).build();
}
public static ResponseResult fail(String msg) {
return new ResponseResultBuilder().code(HTTP_INTERNAL_SERVER_ERROR).msg(msg).build();
}
public static ResponseResult fail(Integer code, String msg) {
return new ResponseResultBuilder().code(code).msg(msg).build();
}
public static String buildSuccessResultStr(Object result) {
if (null == result) {
return "{\"msg\": \"success\",\"code\": " + HTTP_OK + ",\"result\": " + result + "}";
}
return "{\"msg\": \"success\",\"code\": " + HTTP_OK + ",\"result\": \"" + result + "\"}";
}
}
下面的代码是一个自定义异常,这里只是写了两个异常处理,一个是默认的Exception异常,一个是自定义的异常,我们可以将我们自定义的异常放在这个类里,根据异常的不同可以设置返回不同的状态码。
package com.soecode.lyf.exception;
public class ParamInvalidException extends Exception {
public ParamInvalidException() {
}
public ParamInvalidException(String message) {
super(message);
}
public ParamInvalidException(String message, Throwable cause) {
super(message, cause);
}
public ParamInvalidException(Throwable cause) {
super(cause);
}
public ParamInvalidException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
在业务中的应用
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ApiOperation(notes = "获取所有的书", value = "获取所有的书", produces = MediaType.APPLICATION_JSON_VALUE)
private List list() throws ParamInvalidException {
if(1==1){
throw new ParamInvalidException("异常处理成功");
}
List list = bookService.getList();
return list;
}
使用swagger的测试结果如下:
代码改成如下配置,模拟一空指针,程序肯定会出现空指针
测试结果
对于其他的几种异常处理方式可以参见:
https://www.jianshu.com/p/f968b8dcf95a