当前位置: 首页 > news >正文

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

全局异常处理

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

在这里插入图片描述

相关文章:

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

全局异常处理 demo展示 Slf4j RestControllerAdvice public class GlobalExceptionAdvice {ExceptionHandler(RuntimeException.class)public R<Void> handleNotPermissionException(RuntimeException e, HttpServletRequest request) {String requestURI request.get…...

Visual Studio Code 插件的开发、调试及发布完整详细教程

本篇文章主要讲解:Vscode的拓展插件,从环境安装到生成项目文件再到调试及部署发布的完整开发教程。 日期:2023年5月10日 vscode 1.78.1 一、准备node环境及安装yo 项目初始化,优先安装yo、再通过yo创建code及插件项目。 基础条件 需要先安装node,且node环境已经正确安装…...

Qt音视频开发38-ffmpeg视频暂停录制的设计

一、前言 基本上各种播放器提供的录制视频接口,都是只有开始录制和结束录制两个,当然一般用的最多的也是这两个接口,但是实际使用过程中,还有一种可能需要中途暂停录制,暂停以后再次继续录制,将中间部分视频不需要录制,跳过这部分不需要的视频,而且录制的视频文件必须…...

bat脚本、dos命令

bat脚本 bat脚本就是DOS批处理脚本&#xff0c;就是将一系列DOS命令按照一定顺序排列而形成的集合&#xff0c;运行在windows命令行环境上。这个文件的每一行都是一条DOS命令 在命令提示下键入批处理文件的名称&#xff0c;或者双击该批处理文件&#xff0c;系统就会调用Cmd.…...

【星戈瑞】Sulfo-Cyanine5 mal红色荧光Cy5-maleimide

Sulfo-Cyanine5 mal是一种具有强荧光信号的染料&#xff0c;主要应用于生物荧光成像领域。它的化学式为C38H43KN4O9S2&#xff0c;分子量为803.00。这种染料具有良好的水溶性&#xff0c;可在水溶液中稳定存在。它的光学特性包括吸收峰位于646 nm和发射峰位于662 nm&#xff0c…...

Dcip的学习1-计算器

文章目录 前言一、配置安装环境1.1 网址1.2 再次打开需要进行的操作1.3 NodeJS控制台的操作1.4 出现的页面 二、Dcip生成计算器2.1 软件的基本单位 - Unitform中添加内容 2.2 OnleftChange(); 前言 只是为方便学习&#xff0c;不做其他用途&#xff0c; 一、配置安装环境 1.1 …...

ChatGPT使用9大技巧详解

目录 技巧1:To Do and Not To Do 技巧2:增加示例 技巧3:使用引导词,引导模型输出特定内容...

随机变量X,分布函数X~F(x)的理解。

1.随机变量X 1.通常认知的"x"与随机变量X 我们通常意义上的 x 是自变量&#xff0c;y f(x) 中的自变量。 但是 X 更多意义是 对应法则 " f " &#xff0c;X完整写法是 X(ω) ω ∈ Ω。 X这个对应法则&#xff0c;可以将样本点映射到实数轴上。 那么X这…...

11.构造器的查询.分块.聚合

学习要点&#xff1a; 1.构造器查询 2.分块.聚合 本节课我们来开始学习数据库的构造器查询以及分块和聚合查询。 一&#xff0e;构造器查询 1. table()方法引入相应的表&#xff0c;get()方法可以查询当前表的所有数据&#xff1b; //获取全部结果 $users DB::table(users)-&g…...

微服务保护——Sentinel

初识Sentinel 雪崩问题 微服务调用链路中的某个服务故障&#xff0c;引起整个链路中的所有微服务都不可用&#xff0c;这就是雪崩。 解决雪崩问题的常见方式有四种: 超时处理:设定超时时间&#xff0c;请求超过一定时间没有响应就返回错误信息&#xff0c;不会无休止等待舱壁…...

MySQL面试整理

https://houchen-study.oss-cn-hangzhou.aliyuncs.com/%E9%9D%A2%E8%AF%95/MySQL/MySQL%E9%9D%A2%E8%AF%95%E5%A4%A7%E5%85%A8%281%29.pdf 数据库基础知识 为什么要使用数据库&#xff1f; 什么是MySQL&#xff1f; 数据库的三大范式是什么&#xff1f; MySQL有关权限的表…...

Vscode C++环境配置

多文件编译 打开设置搜索coderunner 找到Executor Map 加入-I目录名 目录名/*.cpp 调试 点击调试以后会产生tasks.json文件&#xff0c;加入链接文件和库文件...

matlab小波去噪

本文将为您介绍如何利用MATLAB进行小波去噪处理&#xff0c;并应用于实际数据。小波去噪是一种通过对数据进行小波分解和重构的方法&#xff0c;有效地去除信号中的噪声&#xff0c;提高信号质量。该方法不仅广泛应用于信号处理、图像处理等领域&#xff0c;在实际生产和科研中…...

为什么要采用全网营销策略?全网营销有何优势?

现在市场上有很多全网营销公司&#xff0c;其实很多企业的经理人疑惑全网营销是要干什么&#xff1f;这些公司能干什么&#xff1f;这里小马识途营销顾问给大家做一个整体的解读。 全网营销&#xff0c;概括地说就是在整个互联网&#xff0c;利用各类互联网平台和工具对产品和服…...

prometheus实战之四:alertmanager的部署和配置

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 本文是《prometheus实战》系列的第四篇&#xff0c;在《prometheus实战之三&#xff1a;告警规则》中曾经提到过&#xff0c;整个告警功能分为规则和…...

【Python】glob 包的介绍和使用

glob 是 Python 标准库中的一个模块&#xff0c;它提供了一种查找符合特定模式的路径名的方法&#xff0c;类似于命令行中的 glob 命令。glob 模块用于读取指定路径下的所有符合特定规律的文件名&#xff0c;非常适合用于读取文件夹中的文件列表和操作符合特定规律文件列表。 …...

剑指offer(C++)-JZ48:最长不含重复字符的子字符串(算法-动态规划)

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 题目描述&#xff1a; 请从字符串中找出一个最长的不包含重复字符的子字符串&#xff0c;计算该最长子字符串的长度。 数据范围…...

两阶段最小二乘法

两阶段最小二乘法 文章目录 两阶段最小二乘法[toc]1、ivreg包介绍2 、R语言实现 1、ivreg包介绍 R语言计量包ivreg用以解决线性回归模型的内生性问题。 描述&#xff1a;工具变量估计的线性模型通过两阶段最小二乘(2SLS) 回归或通过稳健回归M估计(2SM)或MM估计(2SMM)。主要的…...

ArcMap创建格网统计图

目录 前言 一、人口数据获取 来源一&#xff1a;中科院地理所公开数据集 来源二&#xff1a;WorldPop数据集 二、人口格网统计步骤 1.创建渔网 2.人口数据处理 2.1 栅格转点 2.2 空间插值——处理人口缺失数据 2.3 空间连接——渔网人口统计 总结 前言 在科研中&am…...

[VAE] Auto-Encoding Variational Bayes

直接看paper看得云里雾里&#xff0c;李沐视频一语道破天机&#xff08;建议从30min左右开始看GAN到Diffusion的串讲&#xff09;。VAE的核心思路就是下面&#xff1a; 做生成&#xff0c;其实就是从随机向量&#xff08;z&#xff09;到目标图像&#xff08;x&#xff09;的过…...

《程序员面试金典(第6版)》面试题 16.19. 水域大小(深度优先搜索,类似棋盘类问题,八皇后的简化版本,C++)

题目描述 你有一个用于表示一片土地的整数矩阵land&#xff0c;该矩阵中每个点的值代表对应地点的海拔高度。若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小&#xff0c;返回值需要从小到…...

Spring 注解之@RestController与@Controller的区别

目录 1&#xff1a;介绍 2&#xff1a;区别 3&#xff1a;总体来说 4&#xff1a;社区地址 1&#xff1a;介绍 RestController 和 Controller 是 Spring MVC 中常用的两个注解&#xff0c;它们都可以用于定义一个控制器类。 2&#xff1a;区别 返回值类型不同&#xff1a;…...

Java中的泛型是什么?如何使用泛型

Java中的泛型是指在定义类、接口和方法时使用类型参数&#xff0c;以使得这些类、接口和方法可以操作多种类型的数据&#xff0c;从而提高代码的重用性和安全性。Java的泛型机制是从JDK5开始引入的&#xff0c;它使得Java程序员能够编写更加通用和类型安全的代码。 什么是泛型…...

【飞行棋】多人游戏-微信小程序开发流程详解

可曾记得小时候玩过的飞行棋游戏&#xff0c;是90后的都有玩过吧&#xff0c;现在重温一下&#xff0c;这是一个可以二到四个人参与的游戏&#xff0c;通过投骰子走棋&#xff0c;一开始靠运气&#xff0c;后面还靠自己选择&#xff0c;谁抢占先机才能赢&#xff0c;还可以和小…...

力扣 146. LRU 缓存

一、题目描述 请你设计并实现一个满足LRU&#xff08;最近最少使用&#xff09;缓存约束的数据结构。 实现 LRUCache 类&#xff1a; LRUCache(int capacity) 以正整数作为容量 capacity 初始化LRU缓存。int get(int key) 如果关键字 key 存在于缓存中&#xff0c;则返回关键…...

关于Oracle SCN的最大阈值

SCN每秒增长的速度跟Oracle的版本有关&#xff0c;在Oracle 11.2.0.2之前是每秒允许最大增长16384&#xff0c;在Oracle 11.2.0.2之后是默认每秒允许增长32768&#xff0c;这个值跟新增的隐含参数_max_reasonable_scn_rate有关&#xff0c;如下所示&#xff1a; NAME …...

Linux多路转接之poll

文章目录 一、poll的认识二、编写poll方案服务器三、poll方案多路转接的总结 一、poll的认识 多路转接技术是在不断更新进步的&#xff0c;一开始多路转接采用的是select方案&#xff0c;但是select方案存在的缺点比较多&#xff0c;所以在此基础上改进&#xff0c;产生了poll…...

Webpack打包流程

轻松了解Webpack 打包流程 Webpack是一个现代的JavaScript应用程序的静态模块打包器。它将多个JavaScript文件打包成一个或多个静态资源文件&#xff0c;以便在浏览器中加载。Webpack将应用程序视为一个依赖项图&#xff0c;其中包括应用程序的所有模块&#xff0c;然后通过该…...

React事件委托

React 事件委托&#xff08;Event Delegation&#xff09;是一种优化事件处理的技术&#xff0c;它通过将事件监听器添加到父级元素&#xff08;而不是子元素&#xff09;来实现。当事件触发时&#xff0c;事件会向上冒泡到父元素&#xff0c;然后在父元素上调用事件处理函数。…...

Notion——构建个人知识库

前言 使用Notion快三年了&#xff0c;它All in one的理念在使用以后确实深有体会&#xff0c;一直想找一个契机将这个软件分享给大家&#xff0c;这款笔记软件在网上已经有很多的教程了&#xff0c;所以在这里我主要想分享框架方面的内容给大家&#xff0c;特别对于学生党、研究…...