1.先看异常是不是@ExceptionHandler中配置的
2.异常可能提示的是A异常,实际上捕捉的B异常,例如JsonParseExcepetion被捕捉,抛出的是HttpMessageNotReadableException异常
源码解析:
AbstractHandlerExceptionResolver中调用resolveException方法
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (!this.shouldApplyTo(request, handler)) {
return null;
} else {
this.prepareResponse(ex, response);
ModelAndView result = this.doResolveException(request, response, handler, ex);
if (result != null) {
if (this.logger.isWarnEnabled() && (this.warnLogger == null || !this.warnLogger.isWarnEnabled())) {
this.logger.warn("Resolved [" + ex + "]" + (result.isEmpty() ? "" : " to " + result));
this.logException(ex, request);
return result;
接下来调用
ExceptionHandlerExceptionResolver的doResolveHandlerMethodException方法
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
Throwable cause = exception.getCause();
if (cause != null) {
// Expose cause as provided argument as well
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
else {
// Otherwise, just the given exception as-is
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
catch (Throwable invocationEx) {
// Any other than the original exception is unintended here,
// probably an accident (e.g. failed assertion or the like).
if (invocationEx != exception && logger.isWarnEnabled()) {
logger.warn("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
// Continue with default processing of the original exception...
return null;
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
else {
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
if (model instanceof RedirectAttributes) {
Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
return mav;
重点重点!!!!
之后调用ExceptionHandlerMethodResolver方法
public Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {
Method method = this.exceptionLookupCache.get(exceptionType);
if (method == null) {
method = getMappedMethod(exceptionType);
this.exceptionLookupCache.put(exceptionType, (method != null ? method : NO_METHOD_FOUND));
return (method != NO_METHOD_FOUND ? method : null);
上述方法之后有cache记录了每次的异常信息
只有系统启动后第一次controller层遇到了异常,才会走getMappedMethod方法:
private Method getMappedMethod(Class<? extends Throwable> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<Class<? extends Throwable>>();
for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
if (!matches.isEmpty()) {
Collections.sort(matches, new ExceptionDepthComparator(exceptionType));
return this.mappedMethods.get(matches.get(0));
else {
return null;
上述方法调用了ExceptionDepthComparator,将接近异常的从深度小到大进行排序,返回最接近的父类异常再进入对应的ExceptionHandler
自己测试的时候一直都没进入getMappedMethod方法,一步一步debug才发现。
1.先看异常是不是@ExceptionHandler中配置的2.异常可能提示的是A异常,实际上捕捉的B异常,例如JsonParseExcepetion被捕捉,抛出的是HttpMessageNotReadableException异常源码解析:AbstractHandlerExceptionResolver中调用resolveException方法 public ModelAndView resolveException(HttpServletRequest request, HttpServl
1、使用aop进行切面拦截异常
2、controller每个方法都用try-catch捕获异常
3、增加一个@RestControllerAdvice标注的类,负责处理我们项目的异常
一般放在一个类中就不会有这种情况了,而我用了两个类全局异常处理类和接口参数校验处理类
还有一种情况是一用力别人的模块,模块中是用了@RestControllerAdvice的类,
多个加了@RestControllerAdvice的类它们会按照类名依次加载,如果前面的类有能处
1、Spring 的 @ExceptionHandler 注解用于统一处理控制层(Ctroller)往外抛的异常。
@ExceptionHandler 注解标注在方法上,此方法会统一处理同一 Controller 下其它 @RequestMapping 注解标注的方法抛出的异常
@ExceptionHandler 注解的属性的 value 值是一个 Class 类型的数组,表示处理哪些类型的异常,为空时表示处理所有异常。
@Excep...
2个服务:A-common 和 BService,各自有异常处理器。
其中A中定义了通用的异常处理器,供所有服务直接调用。
但是B服务由于业务需求需要处理一些A中没有的异常,由于A中存在兜底逻辑:对Exception进行捕获,这个时候就要指定异常处理器的执行顺序。
Spring的异常处理器是根据异常处理器被加载的顺序来顺序执行,比如:A->B->C ,如果B拦截并且处理了某个异常,就会直接抛出,C的异常处理器就执行不到了。
可以通过 @Order 注解来
pulbic classExceptionControllerAdvice{
@ExceptionHandler(NotFoundException.class)
@ResponseB...
是一个组合注解,由、组成,而继承了@Component,因此本质上是个,用于定义,和方法,适用于所有使用方法。
@ControllerAdvice可以指定 Controller 范围
basePackageClasses: 是 basePackages 的一种变形,指定一个或多个 Controller 类,这些类所属的包及其子包下的所有 Controller 都被该 @ControllerAdvice 管理
assignableTypes: 指定一个或多个 Controller 类,这些类被该 @C
@RestControllerAdvice是一个注解,用于定义全局异常处理和全局数据绑定的类。它可以被应用于带有@Controller或@RestController注解的类上,用于统一处理这些类中抛出的异常,并对返回的数据进行统一处理。@RestControllerAdvice主要有以下几个作用:
1. 全局异常处理:通过在@RestControllerAdvice类中定义处理方法,可以统一处理应用程序中抛出的异常。这些异常处理方法使用@ExceptionHandler注解进行标注,并指定要处理的异常类型。当应用程序中发生指定类型的异常时,会自动调用相应的异常处理方法,并返回处理后的结果给客户端。
2. 全局数据绑定:通过在@RestControllerAdvice类中定义@InitBinder注解的方法,可以统一处理请求参数的数据绑定。这些方法使用@InitBinder注解进行标注,并指定要处理的参数类型。在处理请求时,Spring会自动调用相应的数据绑定方法,进行请求参数的数据绑定操作。
3. 全局数据预处理:通过在@RestControllerAdvice类中定义@ModelAttribute注解的方法,可以在处理请求之前对数据进行预处理。这些方法使用@ModelAttribute注解进行标注,并指定要处理的参数类型。在处理请求时,Spring会自动调用相应的数据预处理方法,对请求数据进行处理。
综上所述,@RestControllerAdvice的作用是实现全局异常处理、全局数据绑定和全局数据预处理。