springboot全局异常处理

我们经常写代码的时候会在controller中写大量的try{}catch(exception e){}来响应一些信息给用户以保证接口的完整性。这样在写接口的时候会存在大量重复的代码,即浪费时间,代码看起来又不美观,那么我们是不是可以定义一个异常信息,当出现这种情况的时候直接抛出异常,在异常中响应给用户一些信息。比如,我们写接口的时候,会接收参数,我们要判断参数是否为空,如果不为空的时候那就可以接下来的操作,如果为空的时候我们是不是可以抛出我们自定义的一个异常,在异常中给用户响应一些信息呢?接下来我们就一起看看代码如何实现。
一、使用@RestControllerAdvice和@ExceptionHandler统一处理异常。

  	/**
  	* 这是用来测试的接口
  	*/
  	@RequestMapping("/handle/{id}")
    public String demo(@PathVariable(name="id") String id)throws Exception{

        if(StringUtils.isBlank(id)){
            throw new MyException(false,"10001");
        }
        return "1";
    }
/**
* 这是我们自定义的一个异常,实现RuntimeException
*/
public class MyException extends RuntimeException {

    private boolean success;
    private String errorCode;

    public MyException(boolean success, String errorCode) {
        this.success = success;
        this.errorCode = errorCode;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

    public String getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(String errorCode) {
        this.errorCode = errorCode;
    }
/**
* 这是封住的一个返回数据的格式
*/
public class ResultMessage {

	// 消息标记
	private boolean success;
	// 消息主体
	private Object data;
	// 消息代码
	private String errorCode;
	//错误信息
	private String msg;

	public ResultMessage() {
		super();
	}

	/**
     * 正确数据返回
     * @param
     */
	public ResultMessage(Object data) {
		super();
		this.success = true;
		this.data = data;
		this.errorCode = "00000";
	}
	/**
	 * 错误数据
	 * @param
	 * @param
	 */
	public ResultMessage(boolean success, String errorCode) {
		super();
		this.success = false;
		this.errorCode = errorCode
		;
		this.msg = ErrorCodeEnum.getName(errorCode);
	}

	public boolean isSuccess() {
		return success;
	}

	public void setSuccess(boolean success) {
		this.success = success;
	}

	public Object getData() {
		return data;
	}

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

	public String getErrorCode() {
		return errorCode;
	}

	public void setErrorCode(String errorCode) {
		this.errorCode = errorCode;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
}
/**
* @ClassName: MyControllerAdvice
* @Description: 异常捕获处理
* @Author: zhbin
* @CreateDate: 2019/5/14 14:55
* @Version: 1.0
*/
@RestControllerAdvice
@Slf4j
public class MyControllerAdvice {
    @ExceptionHandler(MyException.class)
    public String myExceptionHandle(MyException e, HttpServletRequest request){
        System.out.println(e);
        return JSON.toJSONString(new ResultMessage(e.isSuccess(),e.getErrorCode()));
    }

}

springboot全局异常处理这样是不是特别方便呢,当参数为空的时候我们直接抛出我们自定义的异常,在异常中响应用户请求,使的我们程序能够走下去。但是有时侯可能即会有我们自定义的异常,也会有系统出错时抛出的异常。接下来我们看一下,该如何处理呢?首先我们改造一下代码。

/**
* @ClassName: MyControllerAdvice
* @Description: 异常捕获处理
* @Author: zhbin
* @CreateDate: 2019/5/14 14:55
* @Version: 1.0
*/
@RestControllerAdvice
@Slf4j
public class MyControllerAdvice {

    @ExceptionHandler(Exception.class)
    public String exceptionHand(Exception e, HttpServletRequest request){

        ResultMessage resultMessage = null;
        System.out.println(request.getMethod()+"----"+request.getServletPath()+"==="+request.getQueryString());
        System.out.println();

        if(e instanceof MyException){

            resultMessage = new ResultMessage(((MyException) e).isSuccess(),((MyException) e).getErrorCode());
        }else{
            resultMessage = new ResultMessage(false,"10002");
        }
        return JSON.toJSONString(resultMessage);
    }
}

我们只需要判断一下异常是不是属于我们自定义的异常(instanceof),如果是响应自定义内容,如果不是响应其他内容就可以。当然我们也可以自定义多种异常来处理我们的义务。这里就不进行演示了。
二、aop进行统一异常处理
我们平时会使用aop来记录日志,那么是否记得aop有一种通知是异常通知,当程序发生异常的时候会进入到异常通知,我们是不是可以在这里面进行全局异常处理呢,接下来我们来看看能否通过aop实现异常处理?
经过测试发现,在异常通知中得不到异常信息,那么是不是aop就处理不了异常呢,其实不然,在环绕通知@Around中可以捕获到异常信息,下面我们看看代码。

/**
* @ClassName: MyControllerAspect
* @Description: aop统一处理异常
* @Author: zhbin
* @CreateDate: 2019/5/16 20:09
* @Version: 1.0
*/
@Aspect
@Component
@Slf4j
public class MyControllerAspect {

    // 切点,定义横切的方法
    @Pointcut("execution(public * com.zhb.exception_handle.controller..*.*(..))")
    public void pcut(){}

    @Around("pcut()")
    public String exciption(ProceedingJoinPoint joinPoint){
        ResultMessage resultMessage = null;
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        log.info("url={}",request.getRequestURL());
        log.info("前置通知------------"+joinPoint);
        try {
            joinPoint.proceed();
            log.info("后置通知-----------"+joinPoint);
        }catch (Throwable e){
             resultMessage = exceptionHandle(e);
            log.info(e+"===异常通知");
        }
        return JSON.toJSONString(resultMessage);
    }

    /**
     * 判断异常类型
     * @param e
     * @return
     */
    public ResultMessage exceptionHandle(Throwable e){

        ResultMessage resultMessage = null;
        if(e instanceof MyException){

            resultMessage = new ResultMessage(((MyException) e).isSuccess(),((MyException) e).getErrorCode());
        }else{
            resultMessage = new ResultMessage(false,"10002");
        }
        return resultMessage;
    }
}

是不是特别方便呢,在环绕通知中捕获到异常后,就可以进行全局异常处理了。和第一种方式一样,只需要判断异常类型后进行相应的操作就可以了。是不是没有想到aop还有这么强大的功能呢。