javascript
Spring MVC 源码-运行调用阶段
這一步步是由請求觸發(fā)的,所以入口為DispatcherServlet 的核心方法為doService(),doService()中的核心邏輯由doDispatch()實現(xiàn),源代碼如下:
/** 中央控制器,控制請求的轉發(fā) **/ 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 {// 1.檢查是否是文件上傳的請求processedRequest = checkMultipart(request);multipartRequestParsed = (processedRequest != request);// Determine handler for the current request.// 2.取得處理當前請求的controller,這里也稱為hanlder,處理器,// 第一個步驟的意義就在這里體現(xiàn)了.這里并不是直接返回controller,// 而是返回的HandlerExecutionChain請求處理器鏈對象,// 該對象封裝了handler和interceptors.mappedHandler = getHandler(processedRequest);// 如果handler為空,則返回404if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// Determine handler adapter for the current request.//3. 獲取處理request的處理器適配器handler adapterHandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// Process last-modified header, if supported by the handler.// 處理 last-modified 請求頭String method = request.getMethod();boolean isGet = "GET".equals(method);if (isGet || "HEAD".equals(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (logger.isDebugEnabled()) {logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);}if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.// 4.實際的處理器處理請求,返回結果視圖對象mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}// 結果視圖對象的處理applyDefaultViewName(processedRequest, mv);mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {dispatchException = ex;}catch (Throwable err) {// As of 4.3, we're processing Errors thrown from handler methods as well,// making them available for @ExceptionHandler methods and other scenarios.dispatchException = new NestedServletException("Handler dispatch failed", err);}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {triggerAfterCompletion(processedRequest, response, mappedHandler, ex);}catch (Throwable err) {triggerAfterCompletion(processedRequest, response, mappedHandler,new NestedServletException("Handler processing failed", err));}finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) {// 請求成功響應之后的方法mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}} }getHandler(processedRequest)方法實際上就是從HandlerMapping 中找到url 和Controller 的對應關系。也就是Map<url,Controller>。我們知道,最終處理Request的是Controller 中的方法,我們現(xiàn)在只是知道了Controller,我們如何確認Controller中處理Request 的方法呢?繼續(xù)往下看。
從Map<urls,beanName>中取得Controller 后,經(jīng)過攔截器的預處理方法,再通過反射獲取該方法上的注解和參數(shù),解析方法和參數(shù)上的注解,然后反射調用方法獲取ModelAndView 結果視圖。最后,調用的就是RequestMappingHandlerAdapter 的handle()中的核心邏輯由handleInternal(request, response, handler)實現(xiàn)。
@Override protected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;checkRequest(request);// Execute invokeHandlerMethod in synchronized block if required.if (this.synchronizeOnSession) {HttpSession session = request.getSession(false);if (session != null) {Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {mav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No HttpSession available -> no mutex necessarymav = invokeHandlerMethod(request, response, handlerMethod);}}else {// No synchronization on session demanded at all...mav = invokeHandlerMethod(request, response, handlerMethod);}if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {prepareResponse(response);}}return mav; }整個處理過程中最核心的邏輯其實就是拼接Controller 的url 和方法的url,與Request的url 進行匹配,找到匹配的方法。
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);if (logger.isDebugEnabled()) {logger.debug("Looking up handler method for path " + lookupPath);}this.mappingRegistry.acquireReadLock();try {HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);if (logger.isDebugEnabled()) {if (handlerMethod != null) {logger.debug("Returning handler method [" + handlerMethod + "]");}else {logger.debug("Did not find handler method for [" + lookupPath + "]");}}return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);}finally {this.mappingRegistry.releaseReadLock();} }通過上面的代碼分析,已經(jīng)可以找到處理Request 的Controller 中的方法了,現(xiàn)在看如何解析該方法上的參數(shù),并反射調用該方法。
/** 獲取處理請求的方法,執(zhí)行并返回結果視圖 **/ @Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod = 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 mavContainer = new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest, mavContainer, invocableMethod);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();if (logger.isDebugEnabled()) {logger.debug("Found concurrent result value [" + result + "]");}invocableMethod = invocableMethod.wrapConcurrentResult(result);}invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();} }invocableMethod.invokeAndHandle()最終要實現(xiàn)的目的就是:完成Request 中的參數(shù)和方法參數(shù)上數(shù)據(jù)的綁定。Spring MVC 中提供兩種Request 參數(shù)到方法中參數(shù)的綁定方式:
1、通過注解進行綁定,@RequestParam。
2、通過參數(shù)名稱進行綁定。
使用注解進行綁定,我們只要在方法參數(shù)前面聲明@RequestParam("name"),就可以將request 中參數(shù)name 的值綁定到方法的該參數(shù)上。使用參數(shù)名稱進行綁定的前提是必須要獲取方法中參數(shù)的名稱,Java 反射只提供了獲取方法的參數(shù)的類型,并沒有提供獲取參數(shù)名稱的方法。SpringMVC 解決這個問題的方法是用asm 框架讀取字節(jié)碼文件,來獲取方法的參數(shù)名稱。asm 框架是一個字節(jié)碼操作框架,關于asm 更多介紹可以參考其官網(wǎng)。個人建議,使用注解來完成參數(shù)綁定,這樣就可以省去asm 框架的讀取字節(jié)碼的操作。
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Invoking '" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +"' with arguments " + Arrays.toString(args));}Object returnValue = doInvoke(args);if (logger.isTraceEnabled()) {logger.trace("Method [" + ClassUtils.getQualifiedMethodName(getMethod(), getBeanType()) +"] returned [" + returnValue + "]");}return returnValue; } private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {MethodParameter[] parameters = getMethodParameters();Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = resolveProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}if (this.argumentResolvers.supportsParameter(parameter)) {try {args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);continue;}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);}throw ex;}}if (args[i] == null) {throw new IllegalStateException("Could not resolve method parameter at index " +parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() +": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));}}return args; }關于asm 框架獲取方法參數(shù)的部分,這里就不再進行分析了。感興趣的小伙伴可以繼續(xù)深入了解這個處理過程。
到這里,方法的參數(shù)值列表也獲取到了,就可以直接進行方法的調用了。整個請求過程中最復雜的一步就是在這里了。到這里整個請求處理過程的關鍵步驟都已了解。理解了Spring MVC 中的請求處理流程,整個代碼還是比較清晰的。最后我們再來梳理一下Spring MVC 核心組件的關聯(lián)關系(如下圖):
最后來一張時序圖:
?
總結
以上是生活随笔為你收集整理的Spring MVC 源码-运行调用阶段的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring MVC 源码-初始化阶段
- 下一篇: Controller 如果能保持单例,尽