前言:项目之前一直用的springboot默认的异常处理BasicErrorController,因业务需要还是需要自定义一个异常处理类,springboot 下有两种方式:继承ErrorController 或 @ControllerAdvice/@RestControllerAdvice, 我自己选用ControllerAdvice注解来自定义全局异常处理,结果发现不生效。
-
网上搜索后可能的问题
- bean没被spring扫描到
用中文搜索ControllerAdvice不生效多半是这个搜索结果 - 项目里有多个ControllerAdvice注解的bean
因为项目不是第一手的,所以我还是去查了一下,可惜不是这个原因 - 自定义异常处理类 extends ResponseEntityExceptionHandler 没有覆写方法,导致被父类处理
- bean没被spring扫描到
我的原因:
最后网上搜了半天,都是以上几种回答,想了下估计这个是自己的问题了,再仔细看了看我的代码:
@ExceptionHandler({ BaseException.class, IllegalArgumentException.class })
public Object runtimeExceptionHandler(HttpServletRequest request, BaseException e) {
//略
}
ExceptionHandler注解的方法我的方法参数是HttpServletRequest request, BaseException e, 看了一下网上的例子是用的Exception,猜测是BaseException不在可选的参数列表里,这个只有改成Exception了,要用的时候手动instance of 判断强转。
后续:
凡是靠猜都是不行的,写代码更是不行,为了记录下这次情况,专门debug看一下是咋调用的:
- 初始化异常处理映射
ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache
private void initExceptionHandlerAdviceCache() {
if (this.getApplicationContext() != null) {
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(this.getApplicationContext());
Iterator var2 = adviceBeans.iterator();
while(var2.hasNext()) {
ControllerAdviceBean adviceBean = (ControllerAdviceBean)var2.next();
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
if (this.logger.isDebugEnabled()) {
int handlerSize = this.exceptionHandlerAdviceCache.size();
int adviceSize = this.responseBodyAdvice.size();
if (handlerSize == 0 && adviceSize == 0) {
this.logger.debug("ControllerAdvice beans: none");
} else {
//springboot 启动时能看到这里输出了我写的自定义异常处理类
this.logger.debug("ControllerAdvice beans: " + handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
}
}
}
}
- 异常处理类的调用
ExceptionHandlerExceptionResolver#doResolveHandlerMethodException
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
ServletInvocableHandlerMethod exceptionHandlerMethod = this.getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
} else {
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
ArrayList exceptions = new ArrayList();
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
}
Throwable cause;
for(Object exToExpose = exception; exToExpose != null; exToExpose = cause != exToExpose ? cause : null) {
exceptions.add(exToExpose);
cause = ((Throwable)exToExpose).getCause();
}
Object[] arguments = new Object[exceptions.size() + 1];
exceptions.toArray(arguments);
arguments[arguments.length - 1] = handlerMethod;
//这里就是调用我们自己的异常处理方法了
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, arguments);
} catch (Throwable var13) {
if (!exceptions.contains(var13) && this.logger.isWarnEnabled()) {
this.logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, var13);
}
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;
}
}
}
- exceptionHandlerMethod.invokeAndHandle内部实现
ServletInvocableHandlerMethod#invokeAndHandle
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//这行代码执行后的 returnValue 就是我们自定义异常处理方法执行后的返回值
Object returnValue = this.invokeForRequest(webRequest, mavContainer, providedArgs);
this.setResponseStatus(webRequest);
if (returnValue == null) {
if (this.isRequestNotModified(webRequest) || this.getResponseStatus() != null || mavContainer.isRequestHandled()) {
this.disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(this.getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(returnValue, this.getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception var6) {
if (logger.isTraceEnabled()) {
logger.trace(this.formatErrorForReturnValue(returnValue), var6);
}
throw var6;
}
}
- this.invokeForRequest 内部实现
@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
//这个地方是关键,就是这个方法里的判断导致之前的异常处理方法失效了
Object[] args = this.getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
return this.doInvoke(args);
}
- 关键地方,就是这里的实现导致失效
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
MethodParameter[] parameters = this.getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
} else {
Object[] args = new Object[parameters.length];
for(int i = 0; i < parameters.length; ++i) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
//因为取不到对应的参数 args[i] == null
if (args[i] == null) {
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
} catch (Exception var10) {
if (logger.isDebugEnabled()) {
String exMsg = var10.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw var10;
}
}
}
return args;
}
}
@Nullable
protected static Object findProvidedArgument(MethodParameter parameter, @Nullable Object... providedArgs) {
if (!ObjectUtils.isEmpty(providedArgs)) {
Object[] var2 = providedArgs;
int var3 = providedArgs.length;
for(int var4 = 0; var4 < var3; ++var4) {
Object providedArg = var2[var4];
//这里进行参数类型校验
if (parameter.getParameterType().isInstance(providedArg)) {
return providedArg;
}
}
}
return null;
}
总结
之所以当时没有生效, 是因为我的注解是这样写的:@ExceptionHandler({ BaseException.class, IllegalArgumentException.class })
而这样当抛IllegalArgumentException异常时也会调用,但是我方法里写的参数是BaseException,当IllegalArgumentException异常发生并调用时只有IllegalArgumentException参数传入,findProvidedArgument方法找不到对应类型参数,所以这里抛异常了,最终没有调用我们的异常处理方法,解决方法就是改为Exception,或者ExceptionHandler注解只能有方法参数的父类异常。
Q.E.D.