前言
日常的开发过程中,我们经常会遇到异常,对于异常处理,大部分人都是try-catch或者直接throw出去不管了,这就导致了代码中四处散落着try-catch的代码,而且经常出现异常处理不妥当或者查找异常困难的情况,如果能将所有的异常处理统一在一个模块去处理那将多么美好,下面我就介绍下java web中比较好的统一处理异常的实践。
1. 统一异常处理的好处
上面说了如果异常处理都是在每个函数中单独处理那么将是非常混乱的,异常处理很难统一管理和排查问题,所以将所有的异常处理集中起来统一处理将是非常好的一个方案,当然这里说的统一处理并不是说必须在一个类中,而是可以集中在一个异常处理模块中,这里介绍下在传统的java web中如何统一处理这样的异常。
2. 如何统一处理异常
统一处理异常处理一般要解决几个问题:1)如何发现所有异常;2)如何捕获到全局的异常;3)如何定义自定义异常。对于如何发现所有异常其实很简单,了解异常栈的同学应该知道,异常时从调用最底层栈开始层层上抛,直到有一个地方catch到并处理,所以我们在编码的时候可以在所有存在异常的地方都是将Exception抛出,而不是去catch单独处理就可以了。捕获全局异常可以通过定义全局捕获处理器来实现Spring也提供了黑科技可以帮助实现。自定义异常其实就是为了解决我们在日常中遇到的非系统异常的问题,我们需要定义一个继承自系统Exception的类,这样全局异常捕获器才能捕获到我们自定义的异常类,并进行处理。
自定义异常类
public class CloudException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* 错误编码
*/
private String errorCode;
/**
* 构造一个基本异常.
*
* @param message
* 信息描述
*/
public CloudException(String message)
{
super(message);
}
/**
* 构造一个基本异常.
*
* @param errorCode
* 错误编码
* @param message
* 信息描述
*/
public CloudException(String errorCode, String message)
{
this(errorCode, message, true);
}
/**
* 构造一个基本异常.
*
* @param errorCode
* 错误编码
* @param message
* 信息描述
*/
public CloudException(String errorCode, String message, Throwable cause)
{
this(errorCode, message, cause, true);
}
/**
* 构造一个基本异常.
*
* @param message
* 信息描述
* @param cause
* 根异常类(可以存入任何异常)
*/
public CloudException(String message, Throwable cause)
{
super(message, cause);
}
public String getErrorCode()
{
return errorCode;
}
public void setErrorCode(String errorCode)
{
this.errorCode = errorCode;
}
}
异常捕获处理类
@ControllerAdvice
public class CloudExceptionHandler {
@ExceptionHandler(value = {Exception.class})
public @ResponseBody RspBean handleOtherExceptions(final Exception ex) {
RspBean rspBean = new RspBean(ex);
if(ex instanceof RuntimeException) {
return new RspBean(ex);
}else if(ex instanceof Exception){
return new RspBean(ex);
}else{
return new RspBean(ex);
}
}
}
其实要解决全局统一异常处理,其实就是要解决如何在一个模块中统一捕获所有的异常,下面我们介绍下异常捕获处理类涉及到知识点:
- @ControllerAdvice:该注解是在Spring3.2后新增的,功能还是非常强大的,相当于对所有的RequestMapping做了一个全局注解,底层实现其实还是通过运行时动态代理,在requestmapping的controller又加了一个动态代理;
- @ExceptionHandler:在类外面加的@ControllerAdvice实现了全局的RequestMapping的动态代理,那要代理哪些内容呢,就要通过类似@ExceptionHandler这样的注解,这里只介绍@ExceptionHandler,支持的其他注解其实还有@InitBinder、@ModelAttribute,这里就不介绍了。@ExceptionHandler异常处理器,此注解的作用是当出现其定义的异常时进行处理的方法,这样就可以把注解的异常处理方法应用到所有controller中,这就实现了全局异常统一处理机制。
这个类写的比较简单,可以针对每个类型的Exception单独写方法去处理,每个方法只需要增加@ExceptionHandler注解就可以了,可以在注解后面增加需要处理的异常类型@ExceptionHandler(RuntimeException.class)
实例代码为了简单说明,就定义了一个方法在方法内部通过if-else去判断不同异常类型。
自定义返回报文
为了统一处理异常我们一般还会统一报文格式,这样我们在返回报文的时候也能够统一报文结构,让调用方处理起来更容易,可以参考下面的返回报文设计,这里错误码定义是静态变量写死,实际使用的时候其实可以将所有的错误码都定义到数据库或者配置文件中,这样更灵活。其中除了返回码、错误码等基础返回报文格式,还有泛型定义的数据,用于填充返回数据包。这样定义好的返回报文类型,其实在正确和异常报文处理的时候都会比较容易,格式也统一。
public class RspBean<T> implements Serializable {
public static final String SUCCESS = "000000";
public static final String FAIL = "000001";
private String errorCode;
private String errorMsg;
private String rtnCode;
private String rtnMsg;
private T data;
public RspBean(){
super();
}
public RspBean(T data){
this.data = data;
this.rtnCode = SUCCESS;
this.rtnMsg = "success";
}
public RspBean(Throwable e){
this.errorCode = FAIL;
this.errorMsg = e.toString();
}
//... 省略setter getter方法
}
网友评论