日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring MVC 源码-运行调用阶段

發布時間:2024/4/13 javascript 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring MVC 源码-运行调用阶段 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這一步步是由請求觸發的,所以入口為DispatcherServlet 的核心方法為doService(),doService()中的核心邏輯由doDispatch()實現,源代碼如下:

/** 中央控制器,控制請求的轉發 **/ 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,處理器,// 第一個步驟的意義就在這里體現了.這里并不是直接返回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 中的方法,我們現在只是知道了Controller,我們如何確認Controller中處理Request 的方法呢?繼續往下看。

從Map<urls,beanName>中取得Controller 后,經過攔截器的預處理方法,再通過反射獲取該方法上的注解和參數,解析方法和參數上的注解,然后反射調用方法獲取ModelAndView 結果視圖。最后,調用的就是RequestMappingHandlerAdapter 的handle()中的核心邏輯由handleInternal(request, response, handler)實現。

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

通過上面的代碼分析,已經可以找到處理Request 的Controller 中的方法了,現在看如何解析該方法上的參數,并反射調用該方法。

/** 獲取處理請求的方法,執行并返回結果視圖 **/ @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()最終要實現的目的就是:完成Request 中的參數和方法參數上數據的綁定。Spring MVC 中提供兩種Request 參數到方法中參數的綁定方式:

1、通過注解進行綁定,@RequestParam。

2、通過參數名稱進行綁定。

使用注解進行綁定,我們只要在方法參數前面聲明@RequestParam("name"),就可以將request 中參數name 的值綁定到方法的該參數上。使用參數名稱進行綁定的前提是必須要獲取方法中參數的名稱,Java 反射只提供了獲取方法的參數的類型,并沒有提供獲取參數名稱的方法。SpringMVC 解決這個問題的方法是用asm 框架讀取字節碼文件,來獲取方法的參數名稱。asm 框架是一個字節碼操作框架,關于asm 更多介紹可以參考其官網。個人建議,使用注解來完成參數綁定,這樣就可以省去asm 框架的讀取字節碼的操作。

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 框架獲取方法參數的部分,這里就不再進行分析了。感興趣的小伙伴可以繼續深入了解這個處理過程。

到這里,方法的參數值列表也獲取到了,就可以直接進行方法的調用了。整個請求過程中最復雜的一步就是在這里了。到這里整個請求處理過程的關鍵步驟都已了解。理解了Spring MVC 中的請求處理流程,整個代碼還是比較清晰的。最后我們再來梳理一下Spring MVC 核心組件的關聯關系(如下圖):

最后來一張時序圖:

?

總結

以上是生活随笔為你收集整理的Spring MVC 源码-运行调用阶段的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。