【源码解析】@ControllerAdvice实现异常捕获与响应增强处理的原理解析

news/2024/11/17 7:34:35/

全局异常处理

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());}
}

RestControllerAdviceControllerAdviceResponseBody的混合注解。

原理解析

系统初始化

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。另外该方法还处理了ModelAttributeInitBinder两个注解。

	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主要是处理了RequestBodyAdviceResponseBodyAdvice

	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().beforeBodyReadgetAdvice().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;}

在这里插入图片描述


http://www.ppmy.cn/news/68885.html

相关文章

栈和队列的相关功能实现及其基础应用

前言&#xff1a;栈和队列是常见的数据结构&#xff0c;它们在计算机科学中被广泛应用。栈和队列都是一些元素的集合&#xff0c;它们的主要区别在于数据的组织方式和访问顺序。在栈中&#xff0c;元素的添加和删除都在同一端进行&#xff0c;称为栈顶&#xff0c;而在队列中&a…

Linux Shell 实现一键部署VMware Workstation

VMware Workstation 前言 VMware Workstation Pro 是业界标准的桌面 Hypervisor&#xff0c;用于在 Linux 或 Windows PC 上运行虚拟机 download VMware_Workstation VMware_Workstation WindowsVMware_Workstation linux文档downloaddownload参考 Linux 各系统下载使用参…

【 LoRa规范及认证】

文章目录 LoRa规范及认证一、LoRaWAN全球区域规范1.美国规范2.中国规范 二、LoRaWAN认证1.LoRaWAN认证常见问题 三、中国LoRa无线电规范四、小结 LoRa规范及认证 ​ 在使用LoRa设备时&#xff0c;必须遵循各区域无线电管理规范&#xff0c;尤其是工作频率、输出功率、占空比这…

贝尔曼福特算法——负权值单源最短路径

title: 贝尔曼福特算法——负权值单源最短路径 date: 2023-05-16 11:42:26 tags: 数据结构与算法 贝尔曼福特算法——负权值单源最短路径 **问题&#xff1a;**具有负权值非环图的单源最短路径算法 git地址&#xff1a;https://github.com/944613709/HIT-Data-Structures-and-A…

Science Advances:宋艳课题组发现经颅近红外激光刺激可提升人类工作记忆

图1. 新闻稿封面 工作记忆——在几秒钟内主动“记住”有用信息的能力——在许多高级认知活动中起着至关重要的作用。由于工作记忆能力的个体差异可以预测流体智力和广泛的认知功能&#xff0c;这使得提高工作记忆能力成为干预和增强的有吸引力的目标。 美国食品及药品管理局声…

SOFA Weekly|SOFAArk 社区会议回顾、Layotto 社区会议预告、社区本周贡献

SOFA WEEKLY | 每周精选 筛选每周精华问答&#xff0c;同步开源进展 欢迎留言互动&#xff5e; SOFAStack&#xff08;Scalable Open Financial Architecture Stack&#xff09;是蚂蚁集团自主研发的金融级云原生架构&#xff0c;包含了构建金融级云原生架构所需的各个组件&am…

wrk泛洪攻击监控脚本

wrk泛洪攻击介绍 WRK泛洪攻击&#xff08;WRK Flood Attack&#xff09;是一种基于WRK工具进行的DDoS攻击&#xff08;分布式拒绝服务攻击&#xff09;。WRK是一个高度并行的HTTP负载生成器&#xff0c;可以模拟大量用户访问一个网站&#xff0c;从而导致该网站服务器瘫痪或失效…

ChatGPT生成Excel统计公式——检查数据是否满足要求

背景需求 有一张表格如下&#xff0c;需要统计每个用户是否在第一到第三周中&#xff0c;每周发文数量都大于等于两篇&#xff0c;是的话给出Yes&#xff0c;否的话给出No 操作流程 给出描述&#xff0c;让ChatGPT生成对应的公式&#xff0c;条件如下&#xff1a; 在excel中…