面试官:小伙子,你画的SpringMVC请求处理过程是从网上抄的吧?

推荐学习肝了十天半月 , 献上纯手绘“Spring/Cloud/Boot/MVC”全家桶脑图消息中间件合集:MQ(ActiveMQ/RabbitMQ/RocketMQ)+Kafka+笔记前言SpringMVC请求处理相信大家都很熟悉了 , 本篇主要是基于SpringMVC处理请求的流程来阅读并调试源码 , 以及解决几个仅靠流程图无法解释的问题 。
本篇使用的Spring版本为5.2.2.RELEASE
九大组件SpringMVC几乎所有的功能都由九大组件来完成 , 所以明白九大组件的作用 , 对于学习SpringMVC来说非常重要 。
/**文件上传解析器*/privateMultipartResolvermultipartResolver;/**区域解析器 , 用于国际化*/privateLocaleResolverlocaleResolver;/**主题解析器*/privateThemeResolverthemeResolver;/**Handler映射信息*/privateListhandlerMappings;/**Handler适配器*/privateListhandlerAdapters;/**Handler执行异常解析器*/privateListhandlerExceptionResolvers;/**请求到视图的转换器*/privateRequestToViewNameTranslatorviewNameTranslator;/**SpringMVC允许重定向时携带参数 , 存在session中 , 用完就销毁 , 所以叫FlashMap*/privateFlashMapManagerflashMapManager;/**视图解析器*/privateListviewResolvers;1234567891011121314151617181920212223242526HandlerMapping:Handler映射信息 , 根据请求携带的url信息查找处理器(Handler) 。 每个请求都需要对应的Handler处理 。 HandlerAdapter:Handler适配器 , SpringMVC没有直接调用处理器(Handler) , 而是通过HandlerAdapter来调用 , 主要是为了统一Handler的调用方式ViewResolver:视图解析器 , 用来将字符串类型的视图名称解析为View类型的视图 。 ViewResolver需要找到渲染所用的模板和所用的技术(也就是视图的类型)进行渲染 , 具体的渲染过程则交由不同的视图自己完成 。 MultipartResolver:文件上传解析器 , 主要用来处理文件上传请求HandlerExceptionResolver:Handler执行异常解析器 , 用来对异常进行统一处理RequestToViewNameTranslator:请求到视图的转换器LocaleResolver:区域解析器 , 用于支持国际化FlashMapManager:SpringMVC允许重定向时携带参数 , 存在session中 , 用完就销毁 , 所以叫FlashMapThemeResolver:主题解析器 , 用于支持不同的主题九大组件中最重的的前三个 , HandlerMapping、HandlerAdapter和ViewResolver , 因为这是阅读源码时 , 避不开的三个组件 。
调试准备搭建一个基本的Springweb项目即可
Controller部分
@ControllerpublicclassIndexController{@RequestMapping("/index/home")publicStringhome(Stringid,Studentstudent,@RequestParam("code")Stringcode){System.out.println(student.getName());return"index";}@ResponseBody@RequestMapping("/index/list")publicStringlist(){return"success";}}Entity部分
publicclassStudent{privateStringname;privateIntegergender;//getter、setter}还是那句话 , Spring源码非常庞大 , 不能只见树木不见森林 , 需要有针对性的阅读 , 所以本篇只需要关注主体流程即可 。
核心方法我们都知道 , SpringMVC有一个用来分发请求的前端控制器DispatcherServlet , 其中用来处理请求的方法就是doService , 该方法定义如下
doService/***ExposestheDispatcherServlet-specificrequestattributesanddelegatesto{@link#doDispatch}*fortheactualdispatching.*/@OverrideprotectedvoiddoService(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{logRequest(request);//Keepasnapshotoftherequestattributesincaseofaninclude,//tobeabletorestoretheoriginalattributesaftertheinclude.MapattributesSnapshot=null;if(WebUtils.isIncludeRequest(request)){attributesSnapshot=newHashMap<>();Enumeration>attrNames=request.getAttributeNames();while(attrNames.hasMoreElements()){StringattrName=(String)attrNames.nextElement();if(this.cleanupAfterInclude||attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)){attributesSnapshot.put(attrName,request.getAttribute(attrName));}}}//Makeframeworkobjectsavailabletohandlersandviewobjects.request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE,getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE,this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE,this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE,getThemeSource());if(this.flashMapManager!=null){FlashMapinputFlashMap=this.flashMapManager.retrieveAndUpdate(request,response);if(inputFlashMap!=null){request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE,Collections.unmodifiableMap(inputFlashMap));}request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE,newFlashMap());request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE,this.flashMapManager);}try{//真正执行的方法doDispatch(request,response);}finally{if(!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()){//Restoretheoriginalattributesnapshot,incaseofaninclude.if(attributesSnapshot!=null){restoreAttributesAfterInclude(request,attributesSnapshot);}}}}doDispatchdoDispatch是doService中真正用来处理请求的方法
/***实际处理请求的方法*/protectedvoiddoDispatch(HttpServletRequestrequest,HttpServletResponseresponse)throwsException{HttpServletRequestprocessedRequest=request;HandlerExecutionChainmappedHandler=null;booleanmultipartRequestParsed=false;WebAsyncManagerasyncManager=WebAsyncUtils.getAsyncManager(request);try{ModelAndViewmv=null;ExceptiondispatchException=null;try{//校验是否是文件上传请求processedRequest=checkMultipart(request);multipartRequestParsed=(processedRequest!=request);//Determinehandlerforthecurrentrequest.//为当前请求找到一个合适的处理器(Handler)//返回值是一个HandlerExecutionChain , 也就是处理器执行链mappedHandler=getHandler(processedRequest);if(mappedHandler==null){noHandlerFound(processedRequest,response);return;}//Determinehandleradapterforthecurrentrequest.//根据HandlerExecutionChain携带的Handler找到合适的HandlerAdapterHandlerAdapterha=getHandlerAdapter(mappedHandler.getHandler());//Processlast-modifiedheader,ifsupportedbythehandler.//处理GET请求的缓存Stringmethod=request.getMethod();booleanisGet="GET".equals(method);if(isGet||"HEAD".equals(method)){longlastModified=ha.getLastModified(request,mappedHandler.getHandler());if(newServletWebRequest(request,response).checkNotModified(lastModified)&&isGet){return;}}//执行拦截器的preHandle方法if(!mappedHandler.applyPreHandle(processedRequest,response)){return;}//Actuallyinvokethehandler.//利用HandlerAdapter来执行Handler里对应的处理方法mv=ha.handle(processedRequest,response,mappedHandler.getHandler());if(asyncManager.isConcurrentHandlingStarted()){return;}//如果没有设置视图 , 则应用默认的视图名applyDefaultViewName(processedRequest,mv);//执行拦截器的postHandle方法mappedHandler.applyPostHandle(processedRequest,response,mv);}catch(Exceptionex){dispatchException=ex;}catch(Throwableerr){//Asof4.3,we'reprocessingErrorsthrownfromhandlermethodsaswell,//makingthemavailablefor@ExceptionHandlermethodsandotherscenarios.dispatchException=newNestedServletException("Handlerdispatchfailed",err);}//根据ModelAndView对象解析视图processDispatchResult(processedRequest,response,mappedHandler,mv,dispatchException);}catch(Exceptionex){triggerAfterCompletion(processedRequest,response,mappedHandler,ex);}catch(Throwableerr){triggerAfterCompletion(processedRequest,response,mappedHandler,newNestedServletException("Handlerprocessingfailed",err));}finally{if(asyncManager.isConcurrentHandlingStarted()){//InsteadofpostHandleandafterCompletionif(mappedHandler!=null){mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest,response);}}else{//Cleanupanyresourcesusedbyamultipartrequest.if(multipartRequestParsed){cleanupMultipart(processedRequest);}}}}该方法就是SpringMVC处理请求的整体流程 , 其中涉及到几个重要的方法 。
getHandler该方法定义如下
/***ReturntheHandlerExecutionChainforthisrequest.*为这个request返回一个HandlerExecutionChain*/@NullableprotectedHandlerExecutionChaingetHandler(HttpServletRequestrequest)throwsException{if(this.handlerMappings!=null){for(HandlerMappingmapping:this.handlerMappings){HandlerExecutionChainhandler=mapping.getHandler(request);if(handler!=null){returnhandler;}}}returnnull;}调试信息如下
getHandlerAdaptergetHandlerAdapter方法定义如下
/***ReturntheHandlerAdapterforthishandlerobject.*@paramhandlerthehandlerobjecttofindanadapterfor*@throwsServletExceptionifnoHandlerAdaptercanbefoundforthehandler.Thisisafatalerror.*/protectedHandlerAdaptergetHandlerAdapter(Objecthandler)throwsServletException{if(this.handlerAdapters!=null){for(HandlerAdapteradapter:this.handlerAdapters){if(adapter.supports(handler)){returnadapter;}}}thrownewServletException("Noadapterforhandler["+handler+"]:TheDispatcherServletconfigurationneedstoincludeaHandlerAdapterthatsupportsthishandler");}调试信息如下
可以看到此处HandlerAdapter真正的实现类是RequestMappingHandlerAdapter 。
processDispatchResultprocessDispatchResult方法主要根据方法执行完成后封装的ModelAndView , 转发到对应页面 , 定义如下
/***Handletheresultofhandlerselectionandhandlerinvocation,whichis*eitheraModelAndVieworanExceptiontoberesolvedtoaModelAndView.*/privatevoidprocessDispatchResult(HttpServletRequestrequest,HttpServletResponseresponse,@NullableHandlerExecutionChainmappedHandler,@NullableModelAndViewmv,@NullableExceptionexception)throwsException{booleanerrorView=false;if(exception!=null){if(exceptioninstanceofModelAndViewDefiningException){logger.debug("ModelAndViewDefiningExceptionencountered",exception);mv=((ModelAndViewDefiningException)exception).getModelAndView();}else{Objecthandler=(mappedHandler!=null?mappedHandler.getHandler():null);mv=processHandlerException(request,response,handler,exception);errorView=(mv!=null);}}//Didthehandlerreturnaviewtorender?if(mv!=null&&!mv.wasCleared()){//主要调用该方法渲染视图render(mv,request,response);if(errorView){WebUtils.clearErrorRequestAttributes(request);}}else{if(logger.isTraceEnabled()){logger.trace("Noviewrendering,nullModelAndViewreturned.");}}if(WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()){//Concurrenthandlingstartedduringaforwardreturn;}if(mappedHandler!=null){//Exception(ifany)isalreadyhandled..mappedHandler.triggerAfterCompletion(request,response,null);}}renderrender方法定义如下
/***RenderthegivenModelAndView.*Thisisthelaststageinhandlingarequest.Itmayinvolveresolvingtheviewbyname.*@parammvtheModelAndViewtorender*@paramrequestcurrentHTTPservletrequest*@paramresponsecurrentHTTPservletresponse*@throwsServletExceptionifviewismissingorcannotberesolved*@throwsExceptionifthere'saproblemrenderingtheview*/protectedvoidrender(ModelAndViewmv,HttpServletRequestrequest,HttpServletResponseresponse)throwsException{//Determinelocaleforrequestandapplyittotheresponse.Localelocale=(this.localeResolver!=null?this.localeResolver.resolveLocale(request):request.getLocale());response.setLocale(locale);Viewview;StringviewName=mv.getViewName();if(viewName!=null){//Weneedtoresolvetheviewname.//根据给定的视图名称 , 解析获取View对象view=resolveViewName(viewName,mv.getModelInternal(),locale,request);if(view==null){thrownewServletException("Couldnotresolveviewwithname'"+mv.getViewName()+"'inservletwithname'"+getServletName()+"'");}}else{//Noneedtolookup:theModelAndViewobjectcontainstheactualViewobject.view=mv.getView();if(view==null){thrownewServletException("ModelAndView["+mv+"]neithercontainsaviewnamenora"+"Viewobjectinservletwithname'"+getServletName()+"'");}}//DelegatetotheViewobjectforrendering.if(logger.isTraceEnabled()){logger.trace("Renderingview["+view+"]");}try{if(mv.getStatus()!=null){response.setStatus(mv.getStatus().value());}view.render(mv.getModelInternal(),request,response);}catch(Exceptionex){if(logger.isDebugEnabled()){logger.
debug("Errorrenderingview["+view+"]",ex);}throwex;}}resolveViewNameresolveViewName方法定义如下
@NullableprotectedViewresolveViewName(StringviewName,@NullableMapmodel,Localelocale,HttpServletRequestrequest)throwsException{if(this.viewResolvers!=null){for(ViewResolverviewResolver:this.viewResolvers){Viewview=viewResolver.resolveViewName(viewName,locale);if(view!=null){returnview;}}}returnnull;}调试信息如下
至此我们就得到了SpringMVC处理请求的完整逻辑
但是 , 有两个重要的问题没有解决 , 那就是:参数绑定和返回值处理 。
因为在编写Controller里面的方法的时候 , 各种类型的参数都有 , SpringMVC是怎么处理不同类型的参数的呢?SpringMVC处理请求完成后 , 一定会返回ModelAndView吗 , 如果加了@ResponseBody注解呢?
参数绑定在整个流程中 , 还有一个最重要的方法 , 那就是真正执行handler的方法 , 参数的绑定和返回值的处理都在这个方法里 , 也就是
//Actuallyinvokethehandler.mv=ha.handle(processedRequest,response,mappedHandler.getHandler());handlehandle方法的作用是根据请求参数 , 执行真正的处理方法 , 并且返回合适的ModelAndView对象 , 也有可能返回null 。 该方法定义如下在AbstractHandlerMethodAdapter类中
/***Thisimplementationexpectsthehandlertobean{@linkHandlerMethod}.*/@Override@NullablepublicfinalModelAndViewhandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{returnhandleInternal(request,response,(HandlerMethod)handler);}可以看到这个方法实现只有一行代码
handleInternal继续深入handleInternal方法
@OverrideprotectedModelAndViewhandleInternal(HttpServletRequestrequest,HttpServletResponseresponse,HandlerMethodhandlerMethod)throwsException{ModelAndViewmav;//校验指定的请求以获取受支持的方法类型(GET、POST等)和所需的sessioncheckRequest(request);//ExecuteinvokeHandlerMethodinsynchronizedblockifrequired.if(this.synchronizeOnSession){HttpSessionsession=request.getSession(false);if(session!=null){Objectmutex=WebUtils.getSessionMutex(session);synchronized(mutex){mav=invokeHandlerMethod(request,response,handlerMethod);}}else{//NoHttpSessionavailable->nomutexnecessarymav=invokeHandlerMethod(request,response,handlerMethod);}}else{//Nosynchronizationonsessiondemandedatall...//真正执行handler的方法mav=invokeHandlerMethod(request,response,handlerMethod);}if(!response.containsHeader(HEADER_CACHE_CONTROL)){if(getSessionAttributesHandler(handlerMethod).hasSessionAttributes()){applyCacheSeconds(response,this.cacheSecondsForSessionAttributeHandlers);}else{prepareResponse(response);}}returnmav;}invokeHandlerMethod继续深入invokeHandlerMethod方法
/***Invokethe{@linkRequestMapping}handlermethodpreparinga{@linkModelAndView}*ifviewresolutionisrequired.*执行@RequestMapping标注的handler方法 , 如果需要解析视图就准备一个ModelAndView*/@NullableprotectedModelAndViewinvokeHandlerMethod(HttpServletRequestrequest,HttpServletResponseresponse,HandlerMethodhandlerMethod)throwsException{ServletWebRequestwebRequest=newServletWebRequest(request,response);try{WebDataBinderFactorybinderFactory=getDataBinderFactory(handlerMethod);ModelFactorymodelFactory=getModelFactory(handlerMethod,binderFactory);//HandlerMethod接口封装执行方法的信息 , 提供对方法参数 , 方法返回值 , 方法注释等的便捷访问 。 ServletInvocableHandlerMethodinvocableMethod=createInvocableHandlerMethod(handlerMethod);if(this.argumentResolvers!=null){invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if(this.returnValueHandlers!=null){invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);//ModelAndViewContainer可以看做ModelAndView的上下文容器 , 关联着Model和View的信息ModelAndViewContainermavContainer=newModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest,mavContainer,invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);AsyncWebRequestasyncWebRequest=WebAsyncUtils.createAsyncWebRequest(request,response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);WebAsyncManagerasyncManager=WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if(asyncManager.hasConcurrentResult()){Objectresult=asyncManager.getConcurrentResult();mavContainer=(ModelAndViewContainer)asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(logger,traceOn->{Stringformatted=LogFormatUtils.formatValue(result,!traceOn);return"Resumewithasyncresult["+formatted+"]";});invocableMethod=invocableMethod.wrapConcurrentResult(result);}//真正执行Handler的方法invocableMethod.invokeAndHandle(webRequest,mavContainer);if(asyncManager.isConcurrentHandlingStarted()){returnnull;}//获取ModelAndeView对象returngetModelAndView(mavContainer,modelFactory,webRequest);}finally{webRequest.requestCompleted();}}invokeAndHandleinvokeAndHandle方法的作用是执行并处理真正响应请求的方法 , 该方法定义如下
/***Invokethemethodandhandlethereturnvaluethroughoneofthe*configured{@linkHandlerMethodReturnValueHandlerHandlerMethodReturnValueHandlers}.*@paramwebRequestthecurrentrequest*@parammavContainertheModelAndViewContainerforthisrequest*@paramprovidedArgs"given"argumentsmatchedbytype(notresolved)*/publicvoidinvokeAndHandle(ServletWebRequestwebRequest,ModelAndViewContainermavContainer,Object...providedArgs)throwsException{//执行handler的方法ObjectreturnValue=https://pcff.toutiao.jxnews.com.cn/p/20200808/invokeForRequest(webRequest,mavContainer,providedArgs);setResponseStatus(webRequest);if(returnValue==null){if(isRequestNotModified(webRequest)||getResponseStatus()!=null||mavContainer.isRequestHandled()){disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}elseif(StringUtils.hasText(getResponseStatusReason())){mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers!=null,"Noreturnvaluehandlers");try{this.returnValueHandlers.handleReturnValue(returnValue,getReturnValueType(returnValue),mavContainer,webRequest);}catch(Exceptionex){if(logger.isTraceEnabled()){logger.trace(formatErrorForReturnValue(returnValue),ex);}throwex;}}invokeForRequest/***Invokethemethodafterresolvingitsargumentvaluesinthecontextofthegivenrequest.*Argumentvaluesarecommonlyresolvedthrough*{@linkHandlerMethodArgumentResolverHandlerMethodArgumentResolvers}.*The{@codeprovidedArgs}parameterhowevermaysupplyargumentvaluestobeuseddirectly,*i.e.withoutargumentresolution.Examplesofprovidedargumentvaluesincludea*{@linkWebDataBinder},a{@linkSessionStatus},orathrownexceptioninstance.
*Providedargumentvaluesarecheckedbeforeargumentresolvers.*Delegatesto{@link#getMethodArgumentValues}andcalls{@link#doInvoke}withthe*resolvedarguments.*@paramrequestthecurrentrequest*@parammavContainertheModelAndViewContainerforthisrequest*@paramprovidedArgs"given"argumentsmatchedbytype,notresolved*@returntherawvaluereturnedbytheinvokedmethod*@throwsExceptionraisedifnosuitableargumentresolvercanbefound,*orifthemethodraisedanexception*@see#getMethodArgumentValues*@see#doInvoke*/@NullablepublicObjectinvokeForRequest(NativeWebRequestrequest,@NullableModelAndViewContainermavContainer,Object...providedArgs)throwsException{//获取参数Object[]args=getMethodArgumentValues(request,mavContainer,providedArgs);if(logger.isTraceEnabled()){logger.trace("Arguments:"+Arrays.toString(args));}//执行returndoInvoke(args);}
真正的执行无非就是通过反射invoke , 所以更重要的是参数是如何绑定的 , 详情就在getMethodArgumentValues方法
getMethodArgumentValuesgetMethodArgumentValues方法用于从request请求中获取真正的参数 , 返回的是Object数组 , 该方法定义如下
/***Getthemethodargumentvaluesforthecurrentrequest,checkingtheprovided*argumentvaluesandfallingbacktotheconfiguredargumentresolvers.*Theresultingarraywillbepassedinto{@link#doInvoke}.*@since5.1.2*/protectedObject[]getMethodArgumentValues(NativeWebRequestrequest,@NullableModelAndViewContainermavContainer,Object...providedArgs)throwsException{//获取方法上所有的参数MethodParameter[]parameters=getMethodParameters();if(ObjectUtils.isEmpty(parameters)){returnEMPTY_ARGS;}Object[]args=newObject[parameters.length];for(inti=0;i<parameters.length;i++){MethodParameterparameter=parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i]=findProvidedArgument(parameter,providedArgs);if(args[i]!=null){continue;}if(!this.resolvers.supportsParameter(parameter)){thrownewIllegalStateException(formatArgumentError(parameter,"Nosuitableresolver"));}try{args[i]=this.resolvers.resolveArgument(parameter,mavContainer,request,this.dataBinderFactory);}catch(Exceptionex){//Leavestacktraceforlater,exceptionmayactuallyberesolvedandhandled...if(logger.isDebugEnabled()){StringexMsg=ex.getMessage();if(exMsg!=null&&!exMsg.contains(parameter.getExecutable().toGenericString())){logger.debug(formatArgumentError(parameter,exMsg));}}throwex;}}returnargs;}
resolveArgumentresolveArgument是真正执行绑定的的方法
/***Iterateoverregistered*{@linkHandlerMethodArgumentResolverHandlerMethodArgumentResolvers}*andinvoketheonethatsupportsit.*@throwsIllegalArgumentExceptionifnosuitableargumentresolverisfound*/@Override@NullablepublicObjectresolveArgument(MethodParameterparameter,@NullableModelAndViewContainermavContainer,NativeWebRequestwebRequest,@NullableWebDataBinderFactorybinderFactory)throwsException{//获取合适的参数解析器HandlerMethodArgumentResolverresolver=getArgumentResolver(parameter);if(resolver==null){thrownewIllegalArgumentException("Unsupportedparametertype["+parameter.getParameterType().getName()+"].supportsParametershouldbecalledfirst.");}//执行参数绑定returnresolver.resolveArgument(parameter,mavContainer,webRequest,binderFactory);}getArgumentResolvergetArgumentResolver该方法用于执行参数的绑定 , 定义如下
/***Findaregistered{@linkHandlerMethodArgumentResolver}thatsupports*thegivenmethodparameter.*/@NullableprivateHandlerMethodArgumentResolvergetArgumentResolver(MethodParameterparameter){HandlerMethodArgumentResolverresult=this.argumentResolverCache.get(parameter);if(result==null){for(HandlerMethodArgumentResolverresolver:this.argumentResolvers){if(resolver.supportsParameter(parameter)){result=resolver;this.argumentResolverCache.put(parameter,result);break;}}}returnresult;}该方法的逻辑就是先从argumentResolver缓存中找到能够执行参数绑定的HandlerMethodArgumentResolver , 如果找不到就从HandlerMethodArgumentResolver找 , SpringMVC支持的HandlerMethodArgumentResolver一共有26种 , 用来解析各种类型的参数
RequestParamMethodArgumentResolver:处理普通参数(基本类型、包装类型、String) , 不管加不加@RequestParam注解ServletModelAttributeMethodProcessor:处理POJO类型的参数 , 比如自定义的Student对象RequestResponseBodyMethodProcessor:处理@RequestBody注解类型的参数有兴趣的同学可以试试更多不同形式的参数
resolveArgument由于不同类型的参数有不同的HandlerMethodArgumentResolver来处理 , 此处选取POJO类型参数的注入实现 , 对应的参数解析类是ModelAttributeMethodProcessor , 其中resolveArgument方法用来解析(绑定)参数方法定义如下
/***Resolvetheargumentfromthemodelorifnotfoundinstantiateitwith*itsdefaultifitisavailable.Themodelattributeisthenpopulated*withrequestvaluesviadatabindingandoptionallyvalidated*if{@code@java.validation.Valid}ispresentontheargument.*@throwsBindExceptionifdatabindingandvalidationresultinanerror*andthenextmethodparameterisnotoftype{@linkErrors}*@throwsExceptionifWebDataBinderinitializationfails*/@Override@NullablepublicfinalObjectresolveArgument(MethodParameterparameter,@NullableModelAndViewContainermavContainer,NativeWebRequestwebRequest,@NullableWebDataBinderFactorybinderFactory)throwsException{Assert.state(mavContainer!=null,"ModelAttributeMethodProcessorrequiresModelAndViewContainer");Assert.state(binderFactory!=null,"ModelAttributeMethodProcessorrequiresWebDataBinderFactory");//获取参数名Stringname=ModelFactory.getNameForParameter(parameter);//获取参数上的ModelAttribute注解ModelAttributeann=parameter.getParameterAnnotation(ModelAttribute.class);if(ann!=null){mavContainer.setBinding(name,ann.binding());}Objectattribute=null;BindingResultbindingResult=null;if(mavContainer.containsAttribute(name)){attribute=mavContainer.getModel().get(name);}else{//Createattributeinstancetry{//创建参数类型的实例(未注入值) , 底层就是通过反射调用构造方法attribute=createAttribute(name,parameter,binderFactory,webRequest);}catch(BindExceptionex){if(isBindExceptionRequired(parameter)){//NoBindingResultparameter->failwithBindExceptionthrowex;}//Otherwise,exposenull/emptyvalueandassociatedBindingResultif(parameter.getParameterType()==Optional.class){attribute=Optional.empty();}bindingResult=ex.getBindingResult();}}if(bindingResult==null){//Beanpropertybindingandvalidation;//skippedincaseofbindingfailureonconstruction.WebDataBinderbinder=binderFactory.createBinder(webRequest,attribute,name);if(binder.getTarget()!=null){if(!mavContainer.isBindingDisabled(name)){//真正执行绑定(值注入)的方法bindRequestParameters(binder,webRequest);}validateIfApplicable(binder,parameter);if(binder.getBindingResult().hasErrors()&&isBindExceptionRequired(binder,parameter)){thrownewBindException(binder.getBindingResult());}}//Valuetypeadaptation,alsocoveringjava.util.Optionalif(!parameter.getParameterType().isInstance(attribute)){attribute=binder.convertIfNecessary(binder.getTarget(),parameter.getParameterType(),parameter);}bindingResult=binder.getBindingResult();}//AddresolvedattributeandBindingResultattheendofthemodelMapbindingResultModel=bindingResult.getModel();mavContainer.removeAttributes(bindingResultModel);mavContainer.addAllAttributes(bindingResultModel);returnattribute;} bindRequestParameters/***Thisimplementationdowncasts{@linkWebDataBinder}to*{@linkServletRequestDataBinder}beforebinding.*@seeServletRequestDataBinderFactory*/@OverrideprotectedvoidbindRequestParameters(WebDataBinderbinder,NativeWebRequestrequest){ServletRequestservletRequest=request.getNativeRequest(ServletRequest.class);Assert.state(servletRequest!=null,"NoServletRequest");ServletRequestDataBinderservletBinder=(ServletRequestDataBinder)binder;//执行绑定的方法servletBinder.bind(servletRequest);}bind继续深入bind方法
publicvoidbind(ServletRequestrequest){//获取所有参数的键值对MutablePropertyValuesmpvs=newServletRequestParameterPropertyValues(request);//处理文件上传请求MultipartRequestmultipartRequest=WebUtils.getNativeRequest(request,MultipartRequest.class);if(multipartRequest!=null){bindMultipart(multipartRequest.getMultiFileMap(),mpvs);}//把url中携带的参数也加入到MutablePropertyValuesaddBindValues(mpvs,request);//执行绑定(注入值)doBind(mpvs);}由于调用层次过深 , 所以无法一步步列出下面的步骤 , doBind方法的原理还是通过调用POJO对象里的setter方法设置值 , 可以查看最终的调试信息
了解了参数的绑定 , 再来看返回值的处理 。
返回值处理invokeAndHandle回到源码invokeAndHandle方法处(ServletInvocableHandlerMethod类中) , 该方法定义如下
/***Invokethemethodandhandlethereturnvaluethroughoneofthe*configured{@linkHandlerMethodReturnValueHandlerHandlerMethodReturnValueHandlers}.*@paramwebRequestthecurrentrequest*@parammavContainertheModelAndViewContainerforthisrequest*@paramprovidedArgs"given"argumentsmatchedbytype(notresolved)*/publicvoidinvokeAndHandle(ServletWebRequestwebRequest,ModelAndViewContainermavContainer,Object...providedArgs)throwsException{ObjectreturnValue=https://pcff.toutiao.jxnews.com.cn/p/20200808/invokeForRequest(webRequest,mavContainer,providedArgs);setResponseStatus(webRequest);if(returnValue==null){if(isRequestNotModified(webRequest)||getResponseStatus()!=null||mavContainer.isRequestHandled()){disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}elseif(StringUtils.hasText(getResponseStatusReason())){mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers!=null,"Noreturnvaluehandlers");try{//真正处理不同类型返回值的方法this.returnValueHandlers.handleReturnValue(returnValue,getReturnValueType(returnValue),mavContainer,webRequest);}catch(Exceptionex){if(logger.isTraceEnabled()){logger.trace(formatErrorForReturnValue(returnValue),ex);}throwex;}}真正处理不同类型的返回值的方法是handleReturnValue方法
handleReturnValue/***Iterateoverregistered{@linkHandlerMethodReturnValueHandlerHandlerMethodReturnValueHandlers}andinvoketheonethatsupportsit.*@throwsIllegalStateExceptionifnosuitable{@linkHandlerMethodReturnValueHandler}isfound.*/@OverridepublicvoidhandleReturnValue(@NullableObjectreturnValue,MethodParameterreturnType,ModelAndViewContainermavContainer,NativeWebRequestwebRequest)throwsException{//根据返回值个返回值类型选取合适的HandlerMethodReturnValueHandlerHandlerMethodReturnValueHandlerhandler=selectHandler(returnValue,returnType);if(handler==null){thrownewIllegalArgumentException("Unknownreturnvaluetype:"+returnType.getParameterType().getName());}//真正的处理返回值handler.handleReturnValue(returnValue,returnType,mavContainer,webRequest);}selectHandler@NullableprivateHandlerMethodReturnValueHandlerselectHandler(@NullableObjectvalue,MethodParameterreturnType){booleanisAsyncValue=https://pcff.toutiao.jxnews.com.cn/p/20200808/isAsyncReturnValue(value,returnType);for(HandlerMethodReturnValueHandlerhandler:this.returnValueHandlers){if(isAsyncValue&&!(handlerinstanceofAsyncHandlerMethodReturnValueHandler)){continue;}if(handler.supportsReturnType(returnType)){returnhandler;}}returnnull;} 事实上 , 用来处理@ResponseBody类型的是RequestResponseBodyMethodProcessor 。
如果对前文参数绑定还有印象的话 , 会发现@RequestBody类型参数绑定也是用的这个类 。
继续跟进RequestResponseBodyMethodProcessor类的handleReturnValue方法
handleReturnValueRequestResponseBodyMethodProcessor类的handleReturnValue方法定义如下
这里设置了一个非常重要的属性requestHandled , 这个属性关系到是否需要返回ModelAndView对象
@OverridepublicvoidhandleReturnValue(@NullableObjectreturnValue,MethodParameterreturnType,ModelAndViewContainermavContainer,NativeWebRequestwebRequest)throwsIOException,HttpMediaTypeNotAcceptableException,HttpMessageNotWritableException{//设置该请求是否已在处理程序中完全处理 , 例如@ResponseBody方法不需要视图解析器 , 此处就可以设置为true 。 //当控制器方法声明类型为ServletResponse或OutputStream的参数时 , 也可以设置此标志为true 。 //这个属性设置成true之后 , 上层getModelAndView获取ModelAndView时会返回Null , 因为不需要视图 。 //默认值为falsemavContainer.setRequestHandled(true);ServletServerHttpRequestinputMessage=createInputMessage(webRequest);ServletServerHttpResponseoutputMessage=createOutputMessage(webRequest);//Tryevenwithnullreturnvalue.ResponseBodyAdvicecouldgetinvolved.//底层就是利用java.io.OutputStreamWriter类把返回值写到网络IOwriteWithMessageConverters(returnValue,returnType,inputMessage,outputMessage);}继续深入writeWithMessageConverters方法 , 一步步调试到最后 , 底层就是利用java.io.OutputStreamWriter类把返回值写到网络IO
总结本文主要从源码的阅读和调试的角度 , 整体的讲解了SpringMVC处理请求的整个流程 , 并且讲解了参数的绑定以及返回值的处理 。 相信大家看完后 , 结合自己的调试信息 , 会对SpringMVC的请求处理过程有一个更深入的理解 。
作者:Sicimike
【面试官:小伙子,你画的SpringMVC请求处理过程是从网上抄的吧?】原文链接:


    推荐阅读