前言
在SpringMVC-RequestMappingHandlerMapping中对web申请到来时如何获取解决该申请的handler进行了剖析,那么获取到web申请对应的handler之后,SpringMVC框架还会依据handler获取其对应的HandlerAdapter
,handler的理论执行就是由HandlerAdapter
实现,并且HandlerAdapter
一共会做三件事件:入参解析,执行解决逻辑,返回值解决。艰深的说就是执行办法前要把办法须要的参数筹备好,而后执行办法,最初办法执行后要对办法返回值进行解决。如果handler是由RequestMappingHandlerMapping
取得,那么执行该handler的HandlerAdapter
应该为RequestMappingHandlerAdapter
,该篇文章将对RequestMappingHandlerAdapter
的入参解析,执行解决逻辑,返回值解决三局部进行学习。
SpringBoot版本:2.4.1
注释
一. RequestMappingHandlerAdapter初始化
首先通过类图认识一下RequestMappingHandlerAdapter
。
由类图可知RequestMappingHandlerAdapter
实现了InitializingBean
接口,因而在容器加载RequestMappingHandlerAdapter
时会执行其实现的afterPropertiesSet()
办法。该办法如下所示。
public void afterPropertiesSet() { initControllerAdviceCache(); 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); }}
afterPropertiesSet()
办法顺次为RequestMappingHandlerAdapter
加载ControllerAdviceBean
相干内容,加载参数解析器,加载返回后果处理器。其中参数解析器和返回后果处理器默认状况下会将SpringMVC框架提供的和用户自定义的一并进行加载,而加载ControllerAdviceBean
相干内容理论就是先获取容器中所有由@ControllerAdvice
注解润饰的bean,而后将这些bean的由@ModelAttribute
注解和@InitBinder
注解润饰的办法加载到RequestMappingHandlerAdapter
中,最初再判断由@ControllerAdvice
注解润饰的bean是否实现了RequestBodyAdvice
或ResponseBodyAdvice
接口,如果是,则将该bean也加载到RequestMappingHandlerAdapter
中。initControllerAdviceCache()
办法如下所示。
由@ControllerAdvice
注解润饰的bean是性能增强型Controller
,通常搭配@ModelAttribute
注解和@InitBinder
注解应用,能够实现:全局数据绑定;全局数据预处理;全局异样解决。
private void initControllerAdviceCache() { if (getApplicationContext() == null) { return; } //将容器中所有带@ControllerAdvice注解的bean获取进去并包装成ControllerAdviceBean对象 List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); List<Object> requestResponseBodyAdviceBeans = new ArrayList<>(); //遍历ControllerAdviceBean对象汇合 for (ControllerAdviceBean adviceBean : adviceBeans) { Class<?> beanType = adviceBean.getBeanType(); if (beanType == null) { throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean); } //获取ControllerAdviceBean对象(包含父对象)和其实现的接口的不禁@RequestMapping注解润饰且由@ModelAttribute注解润饰的办法汇合 Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS); if (!attrMethods.isEmpty()) { this.modelAttributeAdviceCache.put(adviceBean, attrMethods); } //获取ControllerAdviceBean对象(包含父对象)和其实现的接口的由@InitBinder注解润饰的办法汇合 Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS); if (!binderMethods.isEmpty()) { this.initBinderAdviceCache.put(adviceBean, binderMethods); } //判断ControllerAdviceBean对象是否实现了RequestBodyAdvice或ResponseBodyAdvice接口 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
初始化时会将解析参数,参数预处理,执行后果解决等步骤须要用到的bean进行加载。
二. RequestMappingHandlerAdapter参数解析
先通过DispatcherServlet
中的doDispatch()
办法察看handler执行的入口。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } //依据handler获取HandlerAdapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); //如果是GET或者HEAD申请,则获取LastModified工夫戳并判断申请的资源是否扭转 String method = request.getMethod(); boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } //执行拦截器的preHandler办法 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } //执行handler mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { ...... } catch (Throwable err) { ...... } ...... } catch (Exception ex) { ...... } catch (Throwable err) { ...... } finally { ...... }}
handler的执行产生在HandlerAdapter
接口申明的handle()
办法中,由后面类图可知,RequestMappingHandlerAdapter
继承于抽象类AbstractHandlerMethodAdapter
,该抽象类实现了HandlerAdapter
接口,并在其实现的handle()
办法中调用了其申明的形象办法handleInternal()
,因而handler的执行入口是RequestMappingHandlerAdapter
的handleInternal()
办法,该办法如下所示。
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; //如果对反对的HTTP申请办法和会话进行了设置,则校验request是否合乎设置,校验失败抛出异样 checkRequest(request); //如果synchronizeOnSession为true且能从request获取会话对象,则以同步形式执行handler if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { //以不同步形式执行handler mav = invokeHandlerMethod(request, response, handlerMethod); } //response如果没有蕴含Cache-Control响应头,则在这里为response增加Cache-Control响应头 if (!response.containsHeader(HEADER_CACHE_CONTROL)) { //handler如果由@SessionAttributes注解润饰且申明了会话属性,则将response响应头的Cache-Control设置为no-store if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { //否则依据WebContentGenerator的设置来丰盛response prepareResponse(response); } } return mav;}
持续看invokeHandlerMethod()
的实现。
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ServletWebRequest webRequest = new ServletWebRequest(request, response); try { //获取WebDataBinderFactory,顾名思义,该工厂对象用于获取WebDataBinder,以实现request参数与JavaBean的绑定 WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); //获取ModelFactory,用于在handler执行前帮助初始化Model,并在执行后更新Model ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory); //InvocableHandlerMethod继承于HandlerMethod并对其进行了扩大,可能应用参数解析器从request中获取参数并将获取到的参数作为入参调用handler办法 //ServletInvocableHandlerMethod继承于InvocableHandlerMethod并对其进行了扩大,可能应用返回值处理器解决handler办法的返回值以及基于@ResponseStatus注解设置响应状态 ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); //为invocableMethod设置参数解析器 if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } //为invocableMethod设置返回值处理器 if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } //为invocableMethod设置WebDataBinderFactory和ModelFactory invocableMethod.setDataBinderFactory(binderFactory); invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer); //创立ModelAndViewContainer对象 ModelAndViewContainer mavContainer = new ModelAndViewContainer(); //从request中获取Input Flash Attributes并将其加载到ModelAndViewContainer应用的模型Model中 mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request)); //初始化模型Model modelFactory.initModel(webRequest, mavContainer, invocableMethod); //设置是否在重定向时不应用默认模型defaultModel,默认值为false,即重定向时也要应用defaultModel mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect); AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.setTaskExecutor(this.taskExecutor); asyncManager.setAsyncWebRequest(asyncWebRequest); asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); if (asyncManager.hasConcurrentResult()) { Object result = asyncManager.getConcurrentResult(); mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(result, !traceOn); return "Resume with async result [" + formatted + "]"; }); invocableMethod = invocableMethod.wrapConcurrentResult(result); } //执行handler invocableMethod.invokeAndHandle(webRequest, mavContainer); if (asyncManager.isConcurrentHandlingStarted()) { return null; } //更新模型Model,而后创立ModelAndView对象并返回 return getModelAndView(mavContainer, modelFactory, webRequest); } finally { webRequest.requestCompleted(); }}
invokeHandlerMethod()
办法有点长,所做的事件能够概括为首先为执行handler筹备各种必要条件(参数解析器,返回值处理器等),而后执行handler,最初更新模型Model
并生成ModelAndView
对象。这里特地阐明一下下面源码中的ModelAndViewContainer
对象,该对象的源码的正文翻译如下。
记录与模型Model
和视图View
相干的由HandlerMethodArgumentResolvers
和HandlerMethodReturnValueHandlers
在handler办法执行中做出的相干决策。
要了解下面的话,先看一下ModelAndViewContainer
的成员变量。
public class ModelAndViewContainer { //重定向时不应用defaultModel标记位,默认为false(示意重定向时也要应用defaultModel) private boolean ignoreDefaultModelOnRedirect = false; //视图 @Nullable private Object view; //默认模型 private final ModelMap defaultModel = new BindingAwareModelMap(); //重定向模型,重定向场景时能够应用该模型 @Nullable private ModelMap redirectModel; //重定向场景的标记位,示意handler是否返回重定向指令 private boolean redirectModelScenario = false; //http状态码 @Nullable private HttpStatus status; //不应该进行数据绑定的模型属性汇合 private final Set<String> noBinding = new HashSet<>(4); private final Set<String> bindingDisabled = new HashSet<>(4); //标识会话解决是否实现 private final SessionStatus sessionStatus = new SimpleSessionStatus(); //标识handler是否执行实现 private boolean requestHandled = false; }
即ModelAndViewContainer
次要性能如下。
- 保护模型
Model
:非重定向场景下应用defaultModel,重定向场景下且ignoreDefaultModelOnRedirect为true时应用redirectModel; - 保护视图
View
:如果是字符串类型则是逻辑视图; - 保护是否是重定向场景:通过redirectModelScenario属性字段进行标识;
- 保护handler是否执行完:通过requestHandled属性字段进行标识。
因为在前面参数解析和返回后果解决的过程中会将ModelAndViewContainer
对象传递来传递去,其实这个对象做的事件大部分是存储数据和记录状态以不便最初获取ModelAndView
对象,所以在这里先对其进行简要阐明。
回到invokeHandlerMethod()
办法,handler的理论执行是产生在ServletInvocableHandlerMethod
对象的invokeAndHandle()
办法中,持续看这个办法的实现。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //参数解析,执行handler 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; }}
invokeAndHandle()
办法中,调用了ServletInvocableHandlerMethod
的父类InvocableHandlerMethod
的invokeForRequest()
来实现参数解析和执行handler。其实现如下。
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //参数解析 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } //执行handler return doInvoke(args);}
到这里,终于看到了参数解析和执行handler的办法。本大节次要学习参数解析过程,本大节注释开始。
getMethodArgumentValues()
办法是InvocableHandlerMethod
提供的办法,该办法实现两个事件:
- 获取须要执行的handler办法的参数信息,包含:参数名,参数类型,参数注解(如果有的话)等,失去的是一个
MethodParameter[]
数组,每一个handler办法的参数信息都会封装成一个MethodParameter
对象并退出到MethodParameter[]
数组中; - 将handler办法的参数信息与request送入参数解析器,通过参数解析器将须要的参数从request中获取进去。
getMethodArgumentValues()
的实现如下所示。
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //获取须要执行的handler办法的参数信息 MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } Object[] args = new Object[parameters.length]; //遍历所有handler办法的参数信息 for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } //遍历所有参数解析器,寻找可能解析以后handler办法参数的参数解析器,并将其缓存 if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { //将以后handler办法参数信息与request送入参数解析器,通过参数解析器将须要的参数从request中获取进去 args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args;}
InvocableHandlerMethod
的resolvers成员变量是一个HandlerMethodArgumentResolverComposite
对象,该对象和其它参数解析器一样实现了HandlerMethodArgumentResolver
接口,上面看一下其实现的resolveArgument()
办法。
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { //获取handler办法参数对应的参数解析器(个别这里不必遍历全副参数解析器,能够间接从缓存中获取) HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); if (resolver == null) { throw new IllegalArgumentException("Unsupported parameter type [" + parameter.getParameterType().getName() + "]. supportsParameter should be called first."); } //调用参数解析器从request中获取参数 return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);}
在下面resolveArgument()
办法中调用了参数解析器的resolveArgument()
办法,而不同参数解析器的resolveArgument()
办法实现存在肯定差异。
在日常的web开发中,应用@RequestParam
,@PathVariable
,@RequestBody
注解较多,不同注解润饰的参数对应的参数解析器不尽相同,首先看一下这三种注解对应的参数解析器的类图。
由类图能够晓得,@RequestParam
注解和@PathVariable
注解对应的参数解析器继承于AbstractNamedValueMethodArgumentResolver
抽象类,该抽象类实现了HandlerMethodArgumentResolver
接口,因而这两个注解对应的参数解析器调用resolveArgument()
办法理论是调用其父类实现的resolveArgument()
办法。而@RequestBody
注解对应的参数解析器RequestResponseBodyMethodProcessor
对其父类实现的resolveArgument()
办法进行了重写,故该注解对应的参数解析器调用resolveArgument()
办法时理论是调用其自身重写的办法。
先看一下AbstractNamedValueMethodArgumentResolver
对resolveArgument()
办法的实现。
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { //依据handler办法的参数信息创立NamedValueInfo对象 //例如@RequestParam(name = "password", required = true, defaultValue = "admin") String pw //创立的NamedValueInfo对象为NamedValueInfo(name=password, required=true, defaultValue=admin) NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); MethodParameter nestedParameter = parameter.nestedIfOptional(); Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name); if (resolvedName == null) { throw new IllegalArgumentException( "Specified name must not resolve to null: [" + namedValueInfo.name + "]"); } //从request中解析获取参数arg Object arg = resolveName(resolvedName.toString(), nestedParameter, webRequest); if (arg == null) { if (namedValueInfo.defaultValue != null) { arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); } else if (namedValueInfo.required && !nestedParameter.isOptional()) { handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); } arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType()); } else if ("".equals(arg) && namedValueInfo.defaultValue != null) { arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue); } if (binderFactory != null) { //创立WebDataBinder,并执行由@InitBinder注解润饰的办法来初始化WebDataBinder WebDataBinder binder = binderFactory.createBinder(webRequest, null, namedValueInfo.name); try { //应用WebDataBinder实现arg参数的类型转换和绑定 arg = binder.convertIfNecessary(arg, parameter.getParameterType(), parameter); } catch (ConversionNotSupportedException ex) { throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } catch (TypeMismatchException ex) { throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(), namedValueInfo.name, parameter, ex.getCause()); } if (arg == null && namedValueInfo.defaultValue == null && namedValueInfo.required && !nestedParameter.isOptional()) { handleMissingValue(namedValueInfo.name, nestedParameter, webRequest); } } handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest); return arg;}
下面源码中的resolveName()
办法是AbstractNamedValueMethodArgumentResolver
申明的一个形象办法,@RequestParam
注解和@PathVariable
注解对应的参数解析器均对其进行了实现,具体的实现逻辑比较简单,这里不再赘述,总之该办法能够从request中获取指定名称的参数。参数获取到之后,还会通过WebDataBinderFactory
创立WebDataBinder
对象,在创立WebDataBinder
对象的同时还会调用所有由@InitBinder
注解润饰的办法来初始化WebDataBinder
对象,最初应用WebDataBinder
来对刚刚从request中获取到的参数进行类型转换和绑定(即先将arg参数依照咱们的预期进行类型转换,而后再将arg指向转换后的值)。对于WebDataBinder
如何实现类型转换和绑定,将会在前面的一篇文章中专门进行学习,这里不再进行深刻。
上面再看一下RequestResponseBodyMethodProcessor
对resolveArgument()
办法的实现。
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { parameter = parameter.nestedIfOptional(); //从request中获取参数并调用音讯转换器将参数转换为冀望的类型 Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); String name = Conventions.getVariableNameForParameter(parameter); if (binderFactory != null) { //创立WebDataBinder,并执行由@InitBinder注解润饰的办法来初始化WebDataBinder WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); if (arg != null) { //如果handler办法的参数由@Valid或@Validated注解润饰,则对刚刚获取到的参数进行参数校验 validateIfApplicable(binder, parameter); //如果校验不通过则抛出全局异样 if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); } } if (mavContainer != null) { mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); } } return adaptArgumentIfNecessary(arg, parameter);}
readWithMessageConverters()
办法理论就是调用SpringMVC框架提供的(或用户实现的)音讯转换器来实现申请体到参数对象的绑定,并且如果应用了@Valid
或@Validated
注解来进行参数校验,则会在readWithMessageConverters()
办法获取到参数对象后创立WebDataBinder
来实现参数校验,如果校验不通过则会抛出MethodArgumentNotValidException
全局异样,通过解决该异样能够对参数校验不过的状况进行对立解决。上面再看一下readWithMessageConverters()
的实现。
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter, Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); Assert.state(servletRequest != null, "No HttpServletRequest"); ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest); //调用父类的readWithMessageConverters()办法 Object arg = readWithMessageConverters(inputMessage, parameter, paramType); if (arg == null && checkRequired(parameter)) { throw new HttpMessageNotReadableException("Required request body is missing: " + parameter.getExecutable().toGenericString(), inputMessage); } return arg;}
在RequestResponseBodyMethodProcessor
的readWithMessageConverters()
办法中调用了其父类AbstractMessageConverterMethodArgumentResolver
的readWithMessageConverters()
办法,其实现如下所示。
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { MediaType contentType; boolean noContentType = false; try { //获取媒体类型,例如application/json;charset=UTF-8 contentType = inputMessage.getHeaders().getContentType(); } catch (InvalidMediaTypeException ex) { throw new HttpMediaTypeNotSupportedException(ex.getMessage()); } if (contentType == null) { noContentType = true; contentType = MediaType.APPLICATION_OCTET_STREAM; } //获取handler的Class对象 Class<?> contextClass = parameter.getContainingClass(); //targetClass示意handler办法参数类型的Class对象 Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null); if (targetClass == null) { ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter); targetClass = (Class<T>) resolvableType.resolve(); } //获取http申请办法,例如POST HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null); Object body = NO_VALUE; //EmptyBodyCheckingHttpInputMessage实现了HttpInputMessage接口,加强提供了一个hasBody()办法用于判断request是否有申请体 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()) { //在音讯转换器读取申请体前调用所有RequestBodyAdvice接口的beforeBodyRead()办法,以实现一些定制操作 HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType); //音讯转换器读取申请体并将其转换为参数对象 body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) : ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse)); //在音讯转换器实现转换后调用所有RequestBodyAdvice接口的afterBodyRead()办法,以实现一些定制操作 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); } if (body == NO_VALUE) { if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && !message.hasBody())) { return null; } throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes); } MediaType selectedContentType = contentType; Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(theBody, !traceOn); return "Read \"" + selectedContentType + "\" to [" + formatted + "]"; }); return body;}
在AbstractMessageConverterMethodArgumentResolver
的readWithMessageConverters()
办法中,会循环遍历所有音讯转换器并判断其是否能够解决以后申请,同一个申请只会有一个音讯转换器能够解决。音讯转换器读取申请体前,会先获取所有适配以后handler的RequestBodyAdvice
接口,而后循环遍历这些接口并执行其beforeBodyRead()
办法,以实现一些定制解决,同理在音讯转换器实现转换后还会循环遍历这些接口并执行其afterBodyRead()
办法,这里应用了Spring的AOP
:如果是由@ControllerAdvice
注解润饰的RequestBodyAdvice
接口(切面),那么会依据@ControllerAdvice
注解的配置信息来判断该切片是否适配以后handler,如果是不禁@ControllerAdvice
注解润饰的RequestBodyAdvice
接口(切面),则直接判断该切片适配以后handler。
至此,从request中解析@RequestParam
,@PathVariable
和@RequestBody
注解润饰的参数的流程根本剖析结束。
大节:RequestMappingHandlerAdapter
在解析参数前会依据须要执行的handler办法生成一个ServletInvocableHandlerMethod
对象,而后为该对象加载参数解析器,在进行参数解析时,会遍历所有加载的参数解析器并判断遍历到的参数解析器是否解析以后参数,如果能,则应用该参数解析器实现参数解析。
三. RequestMappingHandlerAdapter执行handler办法
在上一大节中曾经晓得,handler办法的参数获取和执行产生在InvocableHandlerMethod
的invokeForRequest()
办法中,再给出该办法的源码如下所示。
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //解析参数 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs); if (logger.isTraceEnabled()) { logger.trace("Arguments: " + Arrays.toString(args)); } //执行handler return doInvoke(args);}
上一大节次要是学习getMethodArgumentValues()
办法做了什么事件,本大节开始学习doInvoke()
办法如何执行handler。doInvoke()
办法实现如下所示。
protected Object doInvoke(Object... args) throws Exception { //如果handler办法是桥接办法则获取其桥接办法,否则获取其自身 Method method = getBridgedMethod(); ReflectionUtils.makeAccessible(method); try { if (KotlinDetector.isSuspendingFunction(method)) { return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args); } //反射形式调用handler办法,getBean()获取handler对象,args是解析失去的参数 return method.invoke(getBean(), args); } catch (IllegalArgumentException ex) { assertTargetBean(method, getBean(), args); String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument"); throw new IllegalStateException(formatInvokeError(text, args), ex); } catch (InvocationTargetException ex) { // Unwrap for HandlerExceptionResolvers ... Throwable targetException = ex.getTargetException(); if (targetException instanceof RuntimeException) { throw (RuntimeException) targetException; } else if (targetException instanceof Error) { throw (Error) targetException; } else if (targetException instanceof Exception) { throw (Exception) targetException; } else { throw new IllegalStateException(formatInvokeError("Invocation failure", args), targetException); } }}
大节:handler办法的执行是通过反射的形式实现的。
四. RequestMappingHandlerAdapter解决返回后果
在第二大节中曾经晓得,返回值解决是产生在ServletInvocableHandlerMethod
对象的invokeAndHandle()
办法中,再给出其实现如下所示。
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { //参数解析,执行handler 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; }}
ServletInvocableHandlerMethod
对象的成员变量returnValueHandlers是一个HandlerMethodReturnValueHandlerComposite
对象,上面看一下该对象的handleReturnValue()
办法。
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { //依据返回值类型匹配返回值处理器 HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType); if (handler == null) { throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName()); } //返回值处理器对返回值进行解决 handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);}
在handleReturnValue()
办法中先是依据返回值类型匹配对应的返回值处理器,而后用匹配到的返回值处理器对返回值进行解决。基于SpringMVC框架进行web开发时,比拟规范的返回值类型应该为ResponseEntity<T>
,该类是由Spring提供的一个继承于HttpEntity<T>
的响应实体类,ResponseEntity<T>
相较于HttpEntity<T>
减少了响应状态码的成员变量,SpringMVC框架专门提供了一个ResponseEntity<T>
对应的返回值处理器HttpEntityMethodProcessor
。同时,如果返回值类型是个别类型或者自定义类型,然而handler办法或者handler由@ResponseBody
注解润饰,那么这种状况对应的返回值处理器是第二大节中呈现过的RequestResponseBodyMethodProcessor
。那么当初以HttpEntityMethodProcessor
和RequestResponseBodyMethodProcessor
这两个返回值处理器为例,对返回值处理器如何解决返回值进行学习。
首先看一下这两个返回值处理器的类图。
HttpEntityMethodProcessor
和RequestResponseBodyMethodProcessor
均实现了HandlerMethodReturnValueHandler
接口,该接口申明了一个用于判断返回值处理器是否能够解决以后返回值的办法supportsReturnType()
。HttpEntityMethodProcessor
对该办法的实现如下。
public boolean supportsReturnType(MethodParameter returnType) { //能够解决类型是HttpEntity且不是RequestEntity的返回值 return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) && !RequestEntity.class.isAssignableFrom(returnType.getParameterType()));}
RequestResponseBodyMethodProcessor
对该办法的实现如下。
public boolean supportsReturnType(MethodParameter returnType) { //如果返回值所在办法或类由@ResponseBody注解润饰,则能够解决 return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class));}
特地阐明一下,每次循环遍历返回值处理器时时,HttpEntityMethodProcessor
会比RequestResponseBodyMethodProcessor
先被遍历到,因而如果handler类或handler办法既由@ResponseBody
注解润饰同时返回值类型还是ResponseEntity
,那么还是会应用HttpEntityMethodProcessor
来解决该返回值。
HandlerMethodReturnValueHandler
接口还申明了一个办法为handleReturnValue()
,该办法会理论的执行解决返回值的逻辑。HttpEntityMethodProcessor
对该办法的实现如下。
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { mavContainer.setRequestHandled(true); if (returnValue == null) { return; } //将HttpServletRequest封装成ServletServerHttpRequest对象 ServletServerHttpRequest inputMessage = createInputMessage(webRequest); //将HttpServletResponse封装成ServletServerHttpResponse对象 ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); Assert.isInstanceOf(HttpEntity.class, returnValue); HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue; HttpHeaders outputHeaders = outputMessage.getHeaders(); HttpHeaders entityHeaders = responseEntity.getHeaders(); if (!entityHeaders.isEmpty()) { //设置响应头字段 entityHeaders.forEach((key, value) -> { if (HttpHeaders.VARY.equals(key) && outputHeaders.containsKey(HttpHeaders.VARY)) { List<String> values = getVaryRequestHeadersToAdd(outputHeaders, entityHeaders); if (!values.isEmpty()) { outputHeaders.setVary(values); } } else { outputHeaders.put(key, value); } }); } if (responseEntity instanceof ResponseEntity) { //从ResponseEntity中获取响应码 int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue(); //设置HttpServletResponse的响应码 outputMessage.getServletResponse().setStatus(returnStatus); if (returnStatus == 200) { HttpMethod method = inputMessage.getMethod(); //如果响应码为200,且http申请办法为GET或者HEAD,且申请的资源没有扭转,此时立刻调用HttpServletResponse的flushBuffer()办法将缓冲区的内容写入客户端 if ((HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method)) && isResourceNotModified(inputMessage, outputMessage)) { outputMessage.flush(); return; } } else if (returnStatus / 100 == 3) { String location = outputHeaders.getFirst("location"); if (location != null) { //如果是重定向场景,将flash attributes增加到session中 saveFlashAttributes(mavContainer, webRequest, location); } } } //调用音讯转换器将返回值写入响应 writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage); outputMessage.flush();}
RequestResponseBodyMethodProcessor
对该办法的实现如下。
public 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); writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);}
能够发现,HttpEntityMethodProcessor
和RequestResponseBodyMethodProcessor
在handleReturnValue()
办法中做了同样的事件:先将ModelAndViewContainer
对象的handler办法是否执行标记设置为true,而后基于HttpServletRequest
和HttpServletResponse
对象创立ServletServerHttpRequest
和ServletServerHttpResponse
对象,最初调用公共父类AbstractMessageConverterMethodProcessor
的writeWithMessageConverters()
办法解决返回值。其中HttpEntityMethodProcessor
多做了两件事件:设置响应头和设置响应码。最初看一下writeWithMessageConverters()
的实现。
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object body; //返回值类型的Class对象 Class<?> valueType; //返回值的泛型类型 Type targetType; if (value instanceof CharSequence) { body = value.toString(); valueType = String.class; targetType = String.class; } else { body = value; valueType = getReturnValueType(body, returnType); targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass()); } //判断返回值类型是否是InputStreamResource或者Resource if (isResourceType(value, returnType)) { outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes"); if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null && outputMessage.getServletResponse().getStatus() == 200) { Resource resource = (Resource) value; try { List<HttpRange> httpRanges = inputMessage.getHeaders().getRange(); outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value()); body = HttpRange.toResourceRegions(httpRanges, resource); valueType = body.getClass(); targetType = RESOURCE_REGION_LIST_TYPE; } catch (IllegalArgumentException ex) { outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength()); outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value()); } } } //selectedMediaType示意响应体的媒体类型 MediaType selectedMediaType = null; //从响应头中获取媒体类型,如果在handler办法中没有手动设置响应体的媒体类型那么这里的contentType为null MediaType contentType = outputMessage.getHeaders().getContentType(); boolean isContentTypePreset = contentType != null && contentType.isConcrete(); //如果在handler办法中设置了响应体媒体类型,则音讯转换器应用设置的媒体类型对返回值进行类型转换 //如果在handler办法中没有设置响应体媒体类型,则从申请中获取客户端承受的媒体类型并选取最合适的媒体类型 if (isContentTypePreset) { if (logger.isDebugEnabled()) { logger.debug("Found 'Content-Type:" + contentType + "' in response"); } selectedMediaType = contentType; } else { HttpServletRequest request = inputMessage.getServletRequest(); List<MediaType> acceptableTypes = getAcceptableMediaTypes(request); List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType); if (body != null && producibleTypes.isEmpty()) { throw new HttpMessageNotWritableException( "No converter found for return value of type: " + valueType); } List<MediaType> mediaTypesToUse = new ArrayList<>(); for (MediaType requestedType : acceptableTypes) { for (MediaType producibleType : producibleTypes) { if (requestedType.isCompatibleWith(producibleType)) { mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType)); } } } if (mediaTypesToUse.isEmpty()) { if (body != null) { throw new HttpMediaTypeNotAcceptableException(producibleTypes); } if (logger.isDebugEnabled()) { logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes); } return; } MediaType.sortBySpecificityAndQuality(mediaTypesToUse); for (MediaType mediaType : mediaTypesToUse) { if (mediaType.isConcrete()) { selectedMediaType = mediaType; break; } else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (logger.isDebugEnabled()) { logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes); } } 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)) { //在音讯转换器转换返回值前调用所有ResponseBodyAdvice接口的beforeBodyWrite()办法,以实现一些定制操作 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) + "]"); //Content-Disposition响应头字段解决 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); }}
在writeWithMessageConverters()
办法中,次要是获取响应体媒体类型,而后调用音讯转换器将返回值依照冀望的媒体类型写入响应体,在写入前还会执行ResponseBodyAdvice
切面对返回值实现一些定制操作。
大节:RequestMappingHandlerAdapter
解决返回值首先会依据返回值类型匹配相应的返回值处理器来解决返回值,在返回值处理器中通常是先获取响应体的媒体类型,而后调用音讯转换器将返回值依照冀望的媒体类型写入响应体。
总结
RequestMappingHandlerAdapter
是基于SpringMVC框架进行web开发应用频次最高的组件之一,RequestMappingHandlerAdapter
在handler办法执行前会应用SpringMVC框架提供的和用户实现的参数解析器从request中获取handler办法须要的参数,而后基于反射形式调用handler办法,最初会应用SpringMVC框架提供的和用户实现的返回值处理器将handler办法的返回值写入响应体中。