淺談SpringMVC請求映射handler源碼解讀
請求映射源碼
首先看一張請求完整流轉(zhuǎn)圖(這里感謝博客園上這位大神的圖,博客地址我忘記了):
前臺發(fā)送給后臺的訪問請求是如何找到對應(yīng)的控制器映射并執(zhí)行后續(xù)的后臺操作呢,其核心為DispatcherServlet.java與HandlerMapper。在spring boot初始化的時候,將會加載所有的請求與對應(yīng)的處理器映射為HandlerMapper組件。我們可以在springMVC的自動配置類中找到對應(yīng)的Bean。
@Bean @Primary @Override public RequestMappingHandlerMapping requestMappingHandlerMapping( @Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager, @Qualifier("mvcConversionService") FormattingConversionService conversionService, @Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) { // Must be @Primary for MvcUriComponentsBuilder to work return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider); } @Bean public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) { WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping( new TemplateAvailabilityProviders(applicationContext), applicationContext, getWelcomePage(), this.mvcProperties.getStaticPathPattern()); welcomePageHandlerMapping.setInterceptors(getInterceptors(mvcConversionService, mvcResourceUrlProvider)); welcomePageHandlerMapping.setCorsConfigurations(getCorsConfigurations()); return welcomePageHandlerMapping; }
請求將首先執(zhí)行FrameworkServlet
下的service方法根據(jù)request請求的method找到對應(yīng)的do**方法。
@Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { //父類根據(jù)method參數(shù)執(zhí)行doGet,doPost,doDelete等 super.service(request, response); } }
而這些do**其都會進入核心方法,以doGet為例。
@Overrideprotected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //核心方法 processRequest(request, response); }
protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { //進入此核心方法 doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); publishRequestHandledEvent(request, response, startTime, failureCause); }
processRequest()
方法中重點在doService(request, response);
,而其核心處理邏輯位于DispatchServletl類重寫的方法,如下。
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { ···· try { //這里為實際分發(fā)控制器的邏輯,其內(nèi)部是找到對應(yīng)的handlerMapper doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } if (requestPath != null) { ServletRequestPathUtils.clearParsedRequestPath(request); } } }
接下來看分發(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 { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // Determine handler adapter for the current request. HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // Process last-modified header, if supported by the handler. 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; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. 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 afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
首先是分析getHandler(),找到對應(yīng)的處理器映射邏輯。
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { if (this.handlerMappings != null) { for (HandlerMapping mapping : this.handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null) { return handler; } } } return null; }
我們將斷點標記在getHandler
方法上時,可以清除看到handlerMappings
,如圖。
這里,用戶請求與處理器的映射關(guān)系都在RequestMapperHandlerMapping
中,而歡迎頁處理請求則在WelcomePageHanderMapping
中進行映射。
以下為RequestMapperHandlerMapping中映射部分截圖,可以看到用戶的所有請求映射這里面都有:
getHandler()后的方法是通過比較request請求中method與HandlerMapper中相同url下的method,再進行唯一性校驗,不通過異常,通過找到唯一的handler。
后續(xù),通過handler找到處理的設(shè)配器,通過適配器得到一個ModelAndView對象,這個對象就是最后返回給前端頁面的對象。
至此,一個請求完整映射到返回前端結(jié)束。
說明:這是實現(xiàn)了FramworkServlet的doService方法,F(xiàn)ramworkServlet繼承自HttpServlet,并且重寫了父類中的doGet(),doPost(),doPut(),doDelete 等方法,在這些重寫的方法里都調(diào)用了 processRquest() 方法做請求處理,進入processRquest()可以看到里面調(diào)用了FramworkServlet中定義的doService() 方法。
到此這篇關(guān)于淺談SpringMVC請求映射handler源碼解讀的文章就介紹到這了,更多相關(guān)SpringMVC請求映射handler 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中HTTP GET方法調(diào)用帶有body的問題解決
這篇文章主要為大家詳細介紹了Java如何解決HTTP GET方法調(diào)用帶有body的問題,文中的示例代碼講解詳細,感興趣的小伙伴可以參考一下2024-02-02關(guān)于Springboot打成JAR包后讀取外部配置文件的問題
這篇文章主要介紹了關(guān)于Springboot打成JAR包后讀取外部配置文件的問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11Springboot AOP對指定敏感字段數(shù)據(jù)加密存儲的實現(xiàn)
本篇文章主要介紹了利用Springboot+AOP對指定的敏感數(shù)據(jù)進行加密存儲以及對數(shù)據(jù)中加密的數(shù)據(jù)的解密的方法,代碼詳細,具有一定的價值,感興趣的小伙伴可以了解一下2021-11-11Spring Boot集成SpringFox 3.0與Pageable參數(shù)處理方法
這篇文章主要介紹了Spring Boot集成SpringFox 3.0與Pageable參數(shù)處理,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-10-10JAVA中@ApiModel和@ApiModelProperty注解實戰(zhàn)代碼
這篇文章主要給大家介紹了關(guān)于JAVA中@ApiModel和@ApiModelProperty注解的相關(guān)資料,@ApiModel注解是用在接口相關(guān)的實體類上的注解,它主要是用來對使用該注解的接口相關(guān)的實體類添加額外的描述信息,常常和@ApiModelProperty注解配合使用,需要的朋友可以參考下2024-03-03Spring boot實現(xiàn)應(yīng)用打包部署的示例
本篇文章主要介紹了Spring boot實現(xiàn)應(yīng)用打包部署的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-11-11