在Java语言中,异常的体系结构大致是:
Throwable
-- Error
-- -- OutOfMemoryError(OOM)
-- Exception
-- -- IOException
-- -- RuntimeException
-- -- -- NullPointerException(NPE)
-- -- -- ClassCastException
-- -- -- IndexOutOfBoundsException
-- -- -- -- ArrayIndexOutOfBoundsException
-- -- -- -- StringIndexOutOfBoundsException
如果调用的方法抛出了“非RuntimeException
”,则必须:
- 当前方法声明抛出此异常
- 使用
try...catch
代码块包裹此方法的调整代码- 真正意义上的“处理了异常”
关于“处理异常”,需要明确的告诉用户“这次操作失败了,失败的原因是XXXXX,你可以通过XXXXX再次尝试,并避免出现此类错误”!
所以,在整个项目,只有Controller才是适合且必须处理异常的组件,因为它可以将错误的描述文本响应到客户端去,而项目中的其它组件(例如Service等)不适合且不应该处理异常,因为它们不可以直接与客户端进行交互,且如果它们处理了异常,则Controller在调用时将无法知道曾经出现过异常,更加无法处理!
在使用Spring MVC框架时,控制器(Controller)可以不处理异常(如果执行过程中出现异常,则自动抛出),框架提供了统一处理异常的机制。
关于统一处理异常:
-
统一处理异常的代码应该编写在专门的类中,并且,在此类上添加
@ControllerAdvice
/@RestControllerAdvice
注解@ControllerAdvice
/@RestControllerAdvice
注解的类中的特定方法将作用于每一次处理请求的过程中- 其实,统一处理异常的代码可以写在某个控制器中,但是,将只能作用于此控制器中各处理请求的方法,无法作用于其它控制器中处理请求的方法
-
在类中自定义方法来处理异常
- 注解:
@ExceptionHandler
- 访问权限:应该
public
- 返回值类型:设计原则可参考控制器中处理请求的方法
- 方法名称:自定义
- 参数列表:必须包含异常类型的参数,且此参数就是Spring MVC框架调用控制器方法时捕获的异常,另外,可按需添加
HttpServerRequest
、HttpServletResponse
等少量限定类型的参数
- 注解:
在同一个项目中,可以有多个以上处理异常的类,或同一个处理异常的类中可以有多个处理异常的方法,只要这些方法处理的异常不冲突即可!并且,这些方法处理的异常允许存在父子级继承关系,例如某个方法处理ServiceException
,另一个方法处理RuntimeException
,当出现ServiceException
,仍会按照处理ServiceException
的方法进行处理!
强烈建议在每个项目中都添加一个处理Throwable
的方法,避免项目出现500
错误!例如:
package cn.tedu.csmall.product.handler;
import cn.tedu.csmall.product.ex.ServiceException;
import cn.tedu.csmall.product.web.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
public JsonResult<Void> handleServiceException(ServiceException e) {
log.debug("处理ServiceException,serviceCode={},message={}", e.getServiceCode(), e.getMessage());
return JsonResult.fail(e);
}
@ExceptionHandler
public JsonResult<Void> handleThrowable(Throwable e) {
log.debug("处理Throwable");
e.printStackTrace();
Integer serviceCode = 99999;
String message = "程序运行过程中出现未知错误,请联系系统管理员!";
return JsonResult.fail(serviceCode, message);
}
}
注意:以上处理Throwable
的方法,并不是真正意义的“处理”了异常,在此方法中,应该通过日志输出异常的详情信息,并且,在后续出现相关异常时,在此类中补充针对这些异常的精准处理!
另外,在@ExceptionHandler
中,可以配置异常类型的参数,此参数是异常类型的数组,用于指定需要处理哪些种类的异常,但是,通常并不需要进行此项配置,因为方法的参数就可以直接表示处理哪种异常!此注解参数大多应用于“多种不相关的异常使用同一种处理方式”的情景!