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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

springMVC源码分析

發布時間:2024/4/14 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springMVC源码分析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本篇將涉及,通過本篇可以對整個springmvc模塊有一個清晰的視角,在日常開發中可以快速定位問題和源碼

  • springMVC啟動初始化過程分析
  • 處理請求過程分析
  • 參數解析、返回值處理
  • springMVC應用啟動初始化過程分析

    初始化過程應該分為三步:

    • 第一步,創建兩個spring的ApplicationContext,配置DispatcherServlet、filter、ContextLoaderListener到servlet容器中
    • 第二步,servlet容器啟動后,調用ContextLoaderListener#contextInitialized初始化必要的bean(如DAO、service相關的bean)
    • 第三步,調用DispatcherServlet#init方法進一步初始化必要的bean(如HandlerMappings、HandlerMappingAdapter等)

    第一步

    主要完成以下工作:

  • 通過配置初始化兩個容器,分別為spring容器、springMVC容器。(關于為什么需要兩個容器,個人認為是為了在兩個DispatcherServlet中共享同一個spring容器)
  • 注冊DispatcherServlet到servlet容器中,對外提供服務,處理客戶端請求
  • 注冊Filter到servlet容器中
  • 第二步

    主要完成以下工作:

  • 初始化父容器
  • 綁定到servletContext,在應用中全局共享
  • 第三步

    在這一步中,分為兩個階段:

  • 初始化屬于springmvc的ApplicationContext、并添加SourceFilteringListener作為spring容器時間監聽器
  • 在完成初始化屬于springmvc的ApplicationContext的工作后,SourceFilteringListener監聽到容器時間,調用onApplicationEvent方法,完成剩余的初始化工作(包括初始化HandlerMappings、HandlerMappingAdapter等屬于MVC領域的bean)
  • SourceFilteringListener的職責

    先來看一下其類關系圖

    SourceFilteringListener作為EventListener的實現類,在servlet的ApplicationContext初始化后,將收到事件回調完成剩余的初始化工作

    protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context); } 復制代碼

    在這些初始化方法中,注冊到spring容器中的bean由注解@EnableWebMVC導入。 導入工作委托給了DelegatingWebMvcConfiguration類

    以上就是springMVC的初始化過程的整體脈絡

    springMVC處理請求過程分析

    整體流程

    整個過程相對比較復雜,整個邏輯在DispatcherServlet#doDispatcher方法中實現,該方法內部將具體工作委托給了RequestMappingHandlerMapping、HandlerExcutionChain、RequestMappingHandlerAdapter、ViewResolver、View等幾個核心類來完成任務。

    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.// 獲取HandlerExcutionChainmappedHandler = getHandler(processedRequest);if (mappedHandler == null || mappedHandler.getHandler() == null) {noHandlerFound(processedRequest, response);return;}// 獲取HandlerAdapter// 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 (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;}// 真正調用HandlerMethod(即Controller方法),內部涉及了參數解析(處理@RequestParams、@PathVariables注解原理)、參數類型轉換、HandlerMethod執行結果處理(@ResponseBody注解原理)這幾個步驟。后面內容我們詳細剖析這個函數的內部機制// 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 afterCompletionif (mappedHandler != null) {mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);}}else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {cleanupMultipart(processedRequest);}}} } 復制代碼

    根據請求尋找對應的HandlerMethod

    具體尋找過程的代碼細節如下:

  • 首先mappingRegistry(類MappingRegistry的實例)實例變量存放了所有的HandlerMethod(在RequestMappingHandlerMapping#afterPropertiesSet()方法中初始化完成,以RequestMappingInfo實例保存在mappingRegistry.urlLookup實例變量中),之后在請求到來時通過請求URI從mappingRegistry獲取
  • 如果在使用路徑參數@PathVariables的時候,需要遍歷所有Controller方法(因為初始化的時候是以/user/{name}作為key保存在urlLookup變量中的,而實際的請求路徑是/user/tom。無法通過this.mappingRegistry.getMappingsByUrl(lookupPath)直接獲取到)。這里會涉及到Ant風格解析。O(n)的時間復雜度。效率極低
  • protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {List<Match> matches = new ArrayList<Match>();List<T> directPathMatches = this.mappingRegistry.getMappingsByUrl(lookupPath);if (directPathMatches != null) {addMatchingMappings(directPathMatches, matches, request);}if (matches.isEmpty()) {// No choice but to go through all mappings...// 如果沒有找到,則遍歷所有Controller方法。這里會涉及到Ant風格解析。O(n)的時間復雜度。效率極低// 在使用路徑參數@PathVariables的時候,會進入到這段邏輯addMatchingMappings(this.mappingRegistry.getMappings().keySet(), matches, request);}if (!matches.isEmpty()) {Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));Collections.sort(matches, comparator);if (logger.isTraceEnabled()) {logger.trace("Found " + matches.size() + " matching mapping(s) for [" +lookupPath + "] : " + matches);}Match bestMatch = matches.get(0);if (matches.size() > 1) {if (CorsUtils.isPreFlightRequest(request)) {return PREFLIGHT_AMBIGUOUS_MATCH;}Match secondBestMatch = matches.get(1);if (comparator.compare(bestMatch, secondBestMatch) == 0) {Method m1 = bestMatch.handlerMethod.getMethod();Method m2 = secondBestMatch.handlerMethod.getMethod();throw new IllegalStateException("Ambiguous handler methods mapped for HTTP path '" +request.getRequestURL() + "': {" + m1 + ", " + m2 + "}");}}// 設置屬性到ServletHttpRequest實例中,在解析@PathVariables時使用handleMatch(bestMatch.mapping, lookupPath, request);return bestMatch.handlerMethod;}else {return handleNoMatch(this.mappingRegistry.getMappings().keySet(), lookupPath, request);} } 復制代碼protected void handleMatch(RequestMappingInfo info, String lookupPath, HttpServletRequest request) {super.handleMatch(info, lookupPath, request);String bestPattern;Map<String, String> uriVariables;Map<String, String> decodedUriVariables;Set<String> patterns = info.getPatternsCondition().getPatterns();if (patterns.isEmpty()) {bestPattern = lookupPath;uriVariables = Collections.emptyMap();decodedUriVariables = Collections.emptyMap();}else {bestPattern = patterns.iterator().next();// 使用Ant風格解析路徑參數uriVariables = getPathMatcher().extractUriTemplateVariables(bestPattern, lookupPath);decodedUriVariables = getUrlPathHelper().decodePathVariables(request, uriVariables);}request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, decodedUriVariables);if (isMatrixVariableContentAvailable()) {Map<String, MultiValueMap<String, String>> matrixVars = extractMatrixVariables(request, uriVariables);request.setAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE, matrixVars);}if (!info.getProducesCondition().getProducibleMediaTypes().isEmpty()) {Set<MediaType> mediaTypes = info.getProducesCondition().getProducibleMediaTypes();request.setAttribute(PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes);} } 復制代碼

    HandlerMethod執行細節

    參數解析以及類型轉換

    主要是通過HandlerMethodArgumentResolverComposite這個特殊的HandlerMethodArgumentResolver聚合了多個HandlerMethodArgumentResolver來解析參數、其內部將調用RequestParamMethodArgumentResolver、PathVariableMapMethodArgumentResolver等來處理參數解析邏輯

    返回結果處理

    主要是通過HandlerMethodReturnValueHandlerComposite這個特殊的HandlerMethodReturnValueHandler聚合了多個HandlerMethodReturnValueHandler來處理返回結果,其內部將調用RequestResponseBodyMethodProcessor處理@ResponseBody、@RestController標記的方法和類來完成RESTful接口的結果返回

    總結

    以上就是整個springmvc的工作流程,包括了初始化、請求處理兩個步驟。在請求處理的過程中又涉及到了參數解析、參數類型轉換、返回結果處理這些流程。由于整個代碼比較繁多,這里就不列舉所有的流程(視圖渲染、異常處理這些細節)通過本篇的主體流程圖可以快速定位到具體的代碼。后續有需要查看更多細節可參考本篇快速定位

    轉載于:https://juejin.im/post/5c2c0a39e51d4563d9209c33

    總結

    以上是生活随笔為你收集整理的springMVC源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

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