全局异常处理
demo展示
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {@ExceptionHandler(RuntimeException.class)public R<Void> handleNotPermissionException(RuntimeException e, HttpServletRequest request) {String requestURI = request.getRequestURI();log.error("请求地址'{}',异常信息'{}'", requestURI, e.getMessage());e.printStackTrace();return R.fail(e.getMessage());}
}
RestControllerAdvice
是ControllerAdvice
和ResponseBody
的混合注解。
原理解析
系统初始化
WebMvcConfigurationSupport
会初始化HandlerExceptionResolver
@Beanpublic HandlerExceptionResolver handlerExceptionResolver(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager) {List<HandlerExceptionResolver> exceptionResolvers = new ArrayList<>();configureHandlerExceptionResolvers(exceptionResolvers);if (exceptionResolvers.isEmpty()) {addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);}extendHandlerExceptionResolvers(exceptionResolvers);HandlerExceptionResolverComposite composite = new HandlerExceptionResolverComposite();composite.setOrder(0);composite.setExceptionResolvers(exceptionResolvers);return composite;}
WebMvcConfigurationSupport#addDefaultHandlerExceptionResolvers
,该方法创建了ExceptionHandlerExceptionResolver
,执行afterPropertiesSet
方法。
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers,ContentNegotiationManager mvcContentNegotiationManager) {ExceptionHandlerExceptionResolver exceptionHandlerResolver = createExceptionHandlerExceptionResolver();exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);exceptionHandlerResolver.setMessageConverters(getMessageConverters());exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());if (jackson2Present) {exceptionHandlerResolver.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));}if (this.applicationContext != null) {exceptionHandlerResolver.setApplicationContext(this.applicationContext);}exceptionHandlerResolver.afterPropertiesSet();exceptionResolvers.add(exceptionHandlerResolver);ResponseStatusExceptionResolver responseStatusResolver = new ResponseStatusExceptionResolver();responseStatusResolver.setMessageSource(this.applicationContext);exceptionResolvers.add(responseStatusResolver);exceptionResolvers.add(new DefaultHandlerExceptionResolver());}
ExceptionHandlerExceptionResolver#afterPropertiesSet
,执行initExceptionHandlerAdviceCache
public void afterPropertiesSet() {// Do this first, it may add ResponseBodyAdvice beansinitExceptionHandlerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}
ExceptionHandlerExceptionResolver#initExceptionHandlerAdviceCache
,对于所有获取到的ControllerAdviceBean
再次封装成ExceptionHandlerMethodResolver
,判断是否存在hasExceptionMappings
,加入到exceptionHandlerAdviceCache
private void initExceptionHandlerAdviceCache() {if (getApplicationContext() == null) {return;}List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());for (ControllerAdviceBean adviceBean : adviceBeans) {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 (logger.isDebugEnabled()) {int handlerSize = this.exceptionHandlerAdviceCache.size();int adviceSize = this.responseBodyAdvice.size();if (handlerSize == 0 && adviceSize == 0) {logger.debug("ControllerAdvice beans: none");}else {logger.debug("ControllerAdvice beans: " +handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");}}}
ExceptionHandlerMethodResolver
构造方法。寻找带有ExceptionHandler
注解的方法,获取注解的value值,即异常处理器处理的异常类。将异常信息和方法的对应关系保存下来。
public ExceptionHandlerMethodResolver(Class<?> handlerType) {for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {addExceptionMapping(exceptionType, method);}}}private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {Method oldMethod = this.mappedMethods.put(exceptionType, method);if (oldMethod != null && !oldMethod.equals(method)) {throw new IllegalStateException("Ambiguous @ExceptionHandler method mapped for [" +exceptionType + "]: {" + oldMethod + ", " + method + "}");}}private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {List<Class<? extends Throwable>> result = new ArrayList<>();detectAnnotationExceptionMappings(method, result);if (result.isEmpty()) {for (Class<?> paramType : method.getParameterTypes()) {if (Throwable.class.isAssignableFrom(paramType)) {result.add((Class<? extends Throwable>) paramType);}}}if (result.isEmpty()) {throw new IllegalStateException("No exception types mapped to " + method);}return result;}private void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {ExceptionHandler ann = AnnotatedElementUtils.findMergedAnnotation(method, ExceptionHandler.class);Assert.state(ann != null, "No ExceptionHandler annotation");result.addAll(Arrays.asList(ann.value()));}
异常处理
DispatcherServlet#processDispatchResult
,判断是否存在异常信息,存在异常信息,调用异常解析器。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;if (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}//...}protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,@Nullable Object handler, Exception ex) throws Exception {// Success and error responses may use different content typesrequest.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);// Check registered HandlerExceptionResolvers...ModelAndView exMv = null;if (this.handlerExceptionResolvers != null) {for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {exMv = resolver.resolveException(request, response, handler, ex);if (exMv != null) {break;}}}//...}
ExceptionHandlerExceptionResolver#doResolveHandlerMethodException
,根据异常获取到处理方法,执行反射。
@Override@Nullableprotected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);if (exceptionHandlerMethod == null) {return null;}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();try {if (logger.isDebugEnabled()) {logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);}Throwable cause = exception.getCause();if (cause != null) {// Expose cause as provided argument as wellexceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);}else {// Otherwise, just the given exception as-isexceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);}}catch (Throwable invocationEx) {// Any other than the original exception (or its cause) is unintended here,// probably an accident (e.g. failed assertion or the like).if (invocationEx != exception && invocationEx != exception.getCause() && logger.isWarnEnabled()) {logger.warn("Failure in @ExceptionHandler " + 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;}}
ExceptionHandlerExceptionResolver#getExceptionHandlerMethod
,创建异常解析器,根据异常解析方法。
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(@Nullable HandlerMethod handlerMethod, Exception exception) {Class<?> handlerType = null;if (handlerMethod != null) {// Local exception handler methods on the controller class itself.// To be invoked through the proxy, even in case of an interface-based proxy.handlerType = handlerMethod.getBeanType();ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);if (resolver == null) {resolver = new ExceptionHandlerMethodResolver(handlerType);this.exceptionHandlerCache.put(handlerType, resolver);}Method method = resolver.resolveMethod(exception);if (method != null) {return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);}// For advice applicability check below (involving base packages, assignable types// and annotation presence), use target class instead of interface-based proxy.if (Proxy.isProxyClass(handlerType)) {handlerType = AopUtils.getTargetClass(handlerMethod.getBean());}}for (Map.Entry<ControllerAdviceBean, ExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {ControllerAdviceBean advice = entry.getKey();if (advice.isApplicableToBeanType(handlerType)) {ExceptionHandlerMethodResolver resolver = entry.getValue();Method method = resolver.resolveMethod(exception);if (method != null) {return new ServletInvocableHandlerMethod(advice.resolveBean(), method);}}}return null;}
ExceptionHandlerMethodResolver#resolveMethod
,获取异常的信息,从mappedMethods
中获取对应的方法,按照ExceptionDepthComparator
排序,获取优先级更高的匹配方法。
@Nullablepublic Method resolveMethod(Exception exception) {return resolveMethodByThrowable(exception);}@Nullablepublic Method resolveMethodByThrowable(Throwable exception) {Method method = resolveMethodByExceptionType(exception.getClass());if (method == null) {Throwable cause = exception.getCause();if (cause != null) {method = resolveMethodByExceptionType(cause.getClass());}}return method;}@Nullablepublic Method resolveMethodByExceptionType(Class<? extends Throwable> exceptionType) {Method method = this.exceptionLookupCache.get(exceptionType);if (method == null) {method = getMappedMethod(exceptionType);this.exceptionLookupCache.put(exceptionType, method);}return method;}@Nullableprivate Method getMappedMethod(Class<? extends Throwable> exceptionType) {List<Class<? extends Throwable>> matches = new ArrayList<>();for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {if (mappedException.isAssignableFrom(exceptionType)) {matches.add(mappedException);}}if (!matches.isEmpty()) {matches.sort(new ExceptionDepthComparator(exceptionType));return this.mappedMethods.get(matches.get(0));}else {return null;}}
ServletInvocableHandlerMethod#invokeAndHandle
,执行异常方法,得到响应结果。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}}
统一响应格式处理
demo展示
@ControllerAdvice(annotations = RestController.class)
public class ResultAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter methodParameter,Class<? extends HttpMessageConverter<?>> converterType) {//true 转换 false 不转换Class<?> parameterType = methodParameter.getParameterType();String returnTypeName = parameterType.getName();return !parameterType.isPrimitive() &&!returnTypeName.equals(String.class.getName()) &&!returnTypeName.equals(R.class.getName());}@Overridepublic Object beforeBodyWrite(Object body,MethodParameter methodParameter,MediaType mediaType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request,ServerHttpResponse response) {return R.ok(body);}
}
原理解析
系统初始化
RequestMappingHandlerAdapter#afterPropertiesSet
,系统启动会执行所有实现了InitializingBean
的Bean的afterPropertiesSet
方法。
@Overridepublic void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}
RequestMappingHandlerAdapter#initControllerAdviceCache
,获取容器中带有ControllerAdvice
注解的Bean,封装成ControllerAdviceBean
。如果是RequestBodyAdvice
或者ResponseBodyAdvice
,加入requestResponseBodyAdviceBeans
。另外该方法还处理了ModelAttribute
和InitBinder
两个注解。
private void initControllerAdviceCache() {if (getApplicationContext() == null) {return;}List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();for (ControllerAdviceBean adviceBean : adviceBeans) {Class<?> beanType = adviceBean.getBeanType();if (beanType == null) {throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);}Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);if (!attrMethods.isEmpty()) {this.modelAttributeAdviceCache.put(adviceBean, attrMethods);}Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);if (!binderMethods.isEmpty()) {this.initBinderAdviceCache.put(adviceBean, binderMethods);}if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {requestResponseBodyAdviceBeans.add(adviceBean);}}if (!requestResponseBodyAdviceBeans.isEmpty()) {this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);}if (logger.isDebugEnabled()) {int modelSize = this.modelAttributeAdviceCache.size();int binderSize = this.initBinderAdviceCache.size();int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {logger.debug("ControllerAdvice beans: none");}else {logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");}}}
RequestMappingHandlerAdapter#getDefaultArgumentResolvers
,加载解析器,会将requestResponseBodyAdvice
加载到RequestResponseBodyMethodProcessor
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);// Annotation-based argument resolution//...resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));//...// Custom argumentsif (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-allresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;}
RequestResponseBodyMethodProcessor
继承了AbstractMessageConverterMethodArgumentResolver
,构造函数中初始化RequestResponseBodyAdviceChain
。
public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,@Nullable List<Object> requestResponseBodyAdvice) {Assert.notEmpty(converters, "'messageConverters' must not be empty");this.messageConverters = converters;this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);}
RequestResponseBodyAdviceChain
主要是处理了RequestBodyAdvice
和ResponseBodyAdvice
。
public RequestResponseBodyAdviceChain(@Nullable List<Object> requestResponseBodyAdvice) {this.requestBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, RequestBodyAdvice.class));this.responseBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, ResponseBodyAdvice.class));}@SuppressWarnings("unchecked")static <T> List<T> getAdviceByType(@Nullable List<Object> requestResponseBodyAdvice, Class<T> adviceType) {if (requestResponseBodyAdvice != null) {List<T> result = new ArrayList<>();for (Object advice : requestResponseBodyAdvice) {Class<?> beanType = (advice instanceof ControllerAdviceBean ?((ControllerAdviceBean) advice).getBeanType() : advice.getClass());if (beanType != null && adviceType.isAssignableFrom(beanType)) {result.add((T) advice);}}return result;}return Collections.emptyList();}
响应处理
RequestResponseBodyMethodProcessor#handleReturnValue
。返回的响应有@ResponseBody
注解,响应处理器就是RequestResponseBodyMethodProcessor
@Overridepublic void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {mavContainer.setRequestHandled(true);ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// Try even with null return value. ResponseBodyAdvice could get involved.writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}
AbstractMessageConverterMethodProcessor#writeWithMessageConverters
,会调用getAdvice().beforeBodyWrite
。
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {Object body;Class<?> valueType;Type targetType;//...if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> converter : this.messageConverters) {GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) converter.getClass(),inputMessage, outputMessage);if (body != null) {Object theBody = body;LogFormatUtils.traceDebug(logger, traceOn ->"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");addContentDispositionHeader(inputMessage, outputMessage);if (genericConverter != null) {genericConverter.write(body, targetType, selectedMediaType, outputMessage);}else {((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);}}else {if (logger.isDebugEnabled()) {logger.debug("Nothing to write: null body");}}return;}}}if (body != null) {Set<MediaType> producibleMediaTypes =(Set<MediaType>) inputMessage.getServletRequest().getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {throw new HttpMessageNotWritableException("No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");}throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);}}
RequestResponseBodyAdviceChain#beforeBodyWrite
,获取匹配的ResponseBodyAdvice
,判断类型是否支持,进行处理。
@Override@Nullablepublic Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,Class<? extends HttpMessageConverter<?>> converterType,ServerHttpRequest request, ServerHttpResponse response) {return processBody(body, returnType, contentType, converterType, request, response);}@Nullableprivate <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,Class<? extends HttpMessageConverter<?>> converterType,ServerHttpRequest request, ServerHttpResponse response) {for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {if (advice.supports(returnType, converterType)) {body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,contentType, converterType, request, response);}}return body;}
RequestResponseBodyAdviceChain#getMatchingAdvice
,如果类型是RequestBodyAdvice
或者ResponseBodyAdvice
,从对应的属性上获取数据。如果是ControllerAdviceBean
,执行resolveBean
方法。
@SuppressWarnings("unchecked")private <A> List<A> getMatchingAdvice(MethodParameter parameter, Class<? extends A> adviceType) {List<Object> availableAdvice = getAdvice(adviceType);if (CollectionUtils.isEmpty(availableAdvice)) {return Collections.emptyList();}List<A> result = new ArrayList<>(availableAdvice.size());for (Object advice : availableAdvice) {if (advice instanceof ControllerAdviceBean) {ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;if (!adviceBean.isApplicableToBeanType(parameter.getContainingClass())) {continue;}advice = adviceBean.resolveBean();}if (adviceType.isAssignableFrom(advice.getClass())) {result.add((A) advice);}}return result;}private List<Object> getAdvice(Class<?> adviceType) {if (RequestBodyAdvice.class == adviceType) {return this.requestBodyAdvice;}else if (ResponseBodyAdvice.class == adviceType) {return this.responseBodyAdvice;}else {throw new IllegalArgumentException("Unexpected adviceType: " + adviceType);}}
ControllerAdviceBean#resolveBean
,获取用spring管理的对应的实体类。
public Object resolveBean() {if (this.resolvedBean == null) {Object resolvedBean = this.obtainBeanFactory().getBean((String)this.beanOrName);if (!this.isSingleton) {return resolvedBean;}this.resolvedBean = resolvedBean;}return this.resolvedBean;}
同理,RequestBodyAdvice
在接收参数的时候,也在做对应的处理。AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters()
。该方法会执行getAdvice().beforeBodyRead
和getAdvice().afterBodyRead
。
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {//...HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);Object body = NO_VALUE;EmptyBodyCheckingHttpInputMessage message;try {message = new EmptyBodyCheckingHttpInputMessage(inputMessage);for (HttpMessageConverter<?> converter : this.messageConverters) {Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();GenericHttpMessageConverter<?> genericConverter =(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :(targetClass != null && converter.canRead(targetClass, contentType))) {if (message.hasBody()) {HttpInputMessage msgToUse =getAdvice().beforeBodyRead(message, parameter, targetType, converterType);body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);}else {body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);}break;}}}catch (IOException ex) {throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);}//...return body;}
RequestResponseBodyAdviceChain#beforeBodyRead
@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {if (advice.supports(parameter, targetType, converterType)) {request = advice.beforeBodyRead(request, parameter, targetType, converterType);}}return request;}@Overridepublic Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {if (advice.supports(parameter, targetType, converterType)) {body = advice.afterBodyRead(body, inputMessage, parameter, targetType, converterType);}}return body;}