整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          最常用的WEB通用基礎-MVC架構,拿走不謝!

          最常用的WEB通用基礎-MVC架構,拿走不謝!

          VC作為WEB程序的通用架構深受廣大WEB程序員的擁護。 更是廣泛成為面試題、考試試題、答辯問題等題材進行使用。

          目前主流的語言框架及WEB系統大部分都是以MVC做為基礎架構進行編寫的。對于初學者而言理解MVC架構與熟悉業務邏輯的編寫能極大的縮短學習一門語言框架的時間。然而初學者僅通過教材或許很難真正的理解MVC的架構及用法,在學習的過程中也可通過分析理解一些經典的語言框架進行學習。(例如:JSP + servlet + javabean)

          初步認知

          MVC模型:作為一種架構型的模式,無法直接實現功能添加,其主要作用是幫助我們對開發結構進行分層,使得代碼結構更佳清晰合理化,將模型(model)、視圖(view)、控制器(controller)進行分離。

          將MVC通俗的理解可以想象成建筑一棟高樓大廈:

          • MVC:最終項目展示結果

          • 模型(Model):建筑材料、建筑工具的集合(WEB項目中的數據模型及函數方法模型)

          • 視圖(View):建筑設計圖紙、建筑展示效果圖(WEB項目中數據的展示形態,優化豐富控制器傳遞過來的數據并進行展示)

          • 控制器(Controller):建筑工程師、施工人員(WEB項目中通過調取對應數據模型及函數方法模型產生數據并傳遞給相關視圖層)

          施工人員(Controller)通過使用建筑工具(Model)將建筑材料(Model)按著設計圖紙(View)進行搭建填裝,最后組裝成一棟高樓大廈。

          深入理解

          模型(Model):模型層的重點在于模塊化,重點思路在于將可復用可集成的代碼封裝成函數方法集成在模型層中方便復用及代碼維護。(例如:用戶登錄驗證,這段代碼可能在多個不同的控制器中都需要使用,若將其封裝成方法寫入模型層,則控制器如果有需求只需導入對應的模型層并以傳參的方式進行調用既可省去大量重復代碼,并且在進行維護的時候也僅需對模型層中的封裝方法進行修改就能實現全局維護)

          視圖(View):視圖層的重點在于數據展示,視圖層的框架要便于數據展示,避免大量邏輯代碼的存在,盡量通過簡單的循環語句將控制層獲取的數據進行展示,所以的數據處理應盡量于控制層與模型層實現。(當然視圖層也可以適當使用模塊化的設計思路,例如:標準WEB網站中,大量網頁頭部及尾部代碼都是重復的,這些重復視圖層代碼可以通過MVC模式的思路封裝到一個單獨的HTML頁面,在需要時通過相關語句載入對應的頁面中,可以大量減少重復代碼。)

          控制器(Controller):控制層主要做為模型層與視圖層的控制中樞。控制器應避免重復視圖層與模型層的工作,將數據的展示封裝至模型層,可復用的數據獲取與數據邏輯處理則在模型層中封裝成函數方法。控制層應做到精簡。

          學編程技術,就到W3Cschool,如果你喜歡我們的文章,可以點擊右上角關注我們;如果你想看到更多IT界的資訊,可以加我們的公眾號。

          公眾號:w3cschoolcn

          學了一遍SpringMVC以后,想著做一個總結,復習一下。復習寫下面的總結的時候才發現,其實自己學得并不徹底、牢固、也沒有學全,視頻跟書本是要結合起來一起,每一位老師的視頻可能提到的東西都不一致,也導致也不是很全面,書本上會講的筆記系統、全面。同時我自己也是一個初學者,下面總結的可能并不完善、正確,希望看到的大神給我指出,在此非常感謝。


          目錄

          • SpringMVC流程及源碼分析
          • 一 、Spring核心模塊
            • 1、核心模塊
            • 2、Spring版本命名規則(補充)
          • 二、SpringMVC流程及原理
            • 1、執行流程
              • 1.1、執行流程
              • 1.2、執行流程說明:
                • 1.2.1、第02、03說明
                • 1.2.2、第04說明
                • 1.2.2、SpringMVC組件說明
                • 1.2.3、SpringMVC詳細流程圖
          • 二、源碼分析
            • 1、初始化
              • 1.1、ApplicationContext
            • 2、前端控制器(中央處理器)DistepcherServlet
              • 2.1、查找處理器映射器HandlerMapping
              • 2.2、根據處理器映射器HandlerMapping返回結果調用處理器適配器HandlerAdapter
              • 2.3、檢查攔截器Interceptor
              • 2.3、處理器適配器HandlerAdapter執行Handler(Controller)返回ModelAndView
              • 2.4、視圖解析器ViewResolver
              • 2.5、視圖View
                • 2.5.1、視圖對象的作用
                • 2.5.2、View接口圖
                • 2.5.3、View的實現類圖
                • 2.5.4、View的UML圖
                • 2.5.5、常用的View視圖類
              • 2.6、其他重要的點
                • 2.6.1、DispatcherServlet.properties
          • 三、引用參考資料
            • 1、引用資料
            • 2、參考資料

          一 、Spring核心模塊

          1、核心模塊

          Spring Web MVC (下文簡稱為 SpringMVC )是 Spring 提供 Web 應用的框架設計,屬于表現層的框架。SpringMVC是Spring框架的一部分。
          Spring框架包括大致六大模塊,核心容器(Core Container)、AOP和設備支持、數據訪問及集成、Web、報文發送、Test

          圖片來源于Spring官網5.0.0.M5:

          ? https://docs.spring.io/spring-framework/docs/5.0.0.M5/spring-framework-reference/html/overview.html#overview-modules

          對于Spring5模塊圖,有2點疑問:
          1、不清楚為什么在Spring官網上5.0版本以后,Release版(穩定版)的都未找到模塊圖,但是在M(里程碑版)版找到 了,如果有人在5.0以后的Release版(穩定版)找到,麻煩給我留個言,謝謝。
          2、在其他博文中看到Spring5模塊結構圖是這樣的:

          挺奇怪這個圖是哪里來的?(路過的大神請指點)

          對于問題2,我在Spring5.2.13.RELEASE GA中,找到了如下所示信息:

          拷貝以上信息:

          Spring Framework Documentation

          Version 5.2.13.RELEASE

          What’s New, Upgrade Notes, Supported Versions, and other topics, independent of release cadence, are maintained externally on the project’s Github Wiki.

          Overview

          history, design philosophy, feedback, getting started.

          Core

          IoC Container, Events, Resources, i18n, Validation, Data Binding, Type Conversion, SpEL, AOP.

          Testing

          Mock Objects, TestContext Framework, Spring MVC Test, WebTestClient.

          Data Access

          Transactions, DAO Support, JDBC, O/R Mapping, XML Marshalling.

          Web Servlet

          Spring MVC, WebSocket, SockJS, STOMP Messaging.

          Web Reactive

          Spring WebFlux, WebClient, WebSocket.

          Integration

          Remoting, JMS, JCA, JMX, Email, Tasks, Scheduling, Caching.

          Languages

          Kotlin, Groovy, Dynamic Languages.

          按照以上信息的Web Servlet、Web Reactive已經是分屬于不同的模塊了。

          • Web Servlet:Spring MVC, WebSocket, SockJS, STOMP Messaging.
          • Web Reactive:Spring WebFlux, WebClient, WebSocket.

          Spring官方文檔:https://spring.io/projects/spring-framework#learn/

          2、Spring版本命名規則(補充)

          上面提到了Spring有不同的版本,在此記錄一下各個版本的意義。

          描述方式

          說明

          含義

          Snapshot

          快照版

          尚不穩定,仍處于開發中的版本

          Release

          穩定版

          功能相對穩定,可以對外發行,但有時間限制

          GA

          正式版

          代表廣泛可用的穩定版(General Availability)

          M

          里程碑版

          (M是Milestone的意思)具有一些全新的功能或是有意義的版本

          RC

          終測版

          Release Candidate(最終測試),即將作為正式版發布

          二、SpringMVC流程及原理

          1、執行流程

          SpringMVC執行流程圖

          圖片來源:三、引用參考資料

          1.1、執行流程

          • 01、用戶發送出請求到前端控制器(中央處理器)DispatcherServlet進行處理。
          • 02、前端控制器DispatcherServlet收到請求后,調用處理器映射器HandlerMapping。
          • 03、處理器映射器HandlerMapping(處理器映射器)根據request請求的URL等信息查找能夠進行處理的Handler,以及相關攔截器interceptor,并構造HandlerExecutionChain執行鏈,然后將構造好的HandlerExecutionChain執行鏈對象返回給前端控制器DispatcherServlet。
          • 04、前端控制器DispatcherServlet根據處理器映射器HandlerMapping的
          • 05、處理器適配器HandlerAdapter經過適配調用具體的處理器(Handler/Controller),即業務中自己寫的Controller。
          • 06、Controller處理完后返回ModelAndView(springmvc的封裝對象,將model和view封裝在一起)給處理器適配器HandlerAdapter;
          • 07、處理器適配器HandlerAdapter將Controller執行結果ModelAndView返回給前端控制器DispatcherServlet。
          • 08、前端控制器DispatcherServlet調用視圖解析器ViewReslover處理ModelAndView。
          • 09、視圖解析器ViewReslover解析后根據邏輯視圖名解析成物理視圖名即具體的頁面地址,生成并返回具體對象View(springmvc封裝對象,是一個接口)。
          • 10、前端控制器DispatcherServlet根據對象View進行視圖渲染,填充Model。
          • 11、前端控制器DispatcherServlet向用戶返回響應

          1.2、執行流程說明:

          1.2.1、第02、03說明

          (1) 處理器映射器:springmvc框架中的一種對象,框架把實現了HandlerMapping接口的類都叫做映射器(多個);

          (2) 處理器映射器作用:根據請求,從springmvc容器對象中獲取處理器對象(MyController controller=ctx.getBean("some")

          (3) 框架把找到的處理器對象放到一個叫做處理器執行鏈(HandlerExecutionChain)的類保存

          (4) HandlerExecutionchain:類中保存著
          ?a:處理器對象(MyController);
          ?b:項目中的所有的攔截器List

          (5) 方法調用:HandlerExecutionChain mappedHandler - getHandler (processedRequest);

          1.2.2、第04說明

          (1) HandlerExecutionChain執行鏈找到對應的處理器映射器HandlerAdapter。
          (2) 處理器適配器:springmvc框架中的對象,需要實現HandlerAdapter接口,
          (3) 處理器適配器作用:執行處理器方法(調用MyController.doSome()得到返回值ModelAndView )
          (4) 前端控制器中調用適配器:HandlerAdapter ha=getHandlerAdapter (mappedHandler.getHandler());
          (5) 執行處理器方法:mv=ha.handle (processedRequest, response, mappedHandler.getHandler());

          第08說明:
          (1) 視圖解析器:springmvc中的對象,需要實現ViewResoler接口(可以有多個)
          (2) 視圖解析器作用:組成視圖完整路徑,使用前綴,后綴。并創建View對象。
          (3) view是一個接口,表示視圖的,在框架中jsp,htm1不是string表示,而是使用view和他的實現類表示視圖。

          InternalResourceview:視圖類,表示jsp文件,視圖解析器會創建InternalResourceView類對象。 這個對象的里面,有一個屬性url-/WEB-INF/view/show.jsp

          1.2.2、SpringMVC組件說明

          • (1). 前端控制器(DispatcherServlet):接收請求,響應結果,相當于電腦的CPU。
          • (2). 處理器映射器(HandlerMapping):根據URL去查找處理器.
          • (3). 處理器(Handler):(需要程序員去寫代碼處理邏輯的).
          • (4). 處理器適配器(HandlerAdapter):會把處理器包裝成適配器,這樣就可以支持多種類型的處理器,類比筆記本的適配器(適配器模式的應用).
          • (5). 視圖解析器(ViewResovler):進行視圖解析,多返回的字符串,進行處理,可以解析成對應的頁面.

          1.2.3、SpringMVC詳細流程圖

          綜上所述,總結下SpringMVC的詳細流程圖:


          圖片來源:三、引用參考資料

          二、源碼分析

          以下源碼來源jar包:spring-webmvc-5.25.RELEASE.jar

          1、初始化

          1.1、ApplicationContext

          ? ApplicationContext初始化入口類:ApplicationObjectSupport的setApplicationContext方法,setApplicationContext方法中核心部分就是初始化容器initApplicationContext(context),子類AbstractDetectingUrlHandlerMapping實現了該方法。
          類圖:


          UML圖:


          ? RequestMappingHandlerMapping ,用于注解@Controller,@RequestMapping來定義controller.
          初始化時,3個類的大致分工如下:

          • AbstractHandlerMethodMapping定義整個算法流程;
          • RequestMappingInfoHandlerMapping提供匹配條件RequestMappingInfo的解析處理;
          • RequestMappingHandlerMapping根據@RequestMapping注解生成 RequestMappingInfo,同時提供isHandler實現

          2、前端控制器(中央處理器)DistepcherServlet

          ? 從上面的流程圖可以看到前端控制器(中央處理器)DistepcherServlet是SpringMVC核心,查看DistepcherServlet類的繼承情況。
          UML圖:
          ![2021022601-08-DispatcherServlet UML圖](https://gitee.com/chuchq/blogs-gallery/raw/master/images / 2021/2021022601-08-DispatcherServlet UML圖.png)
          從繼承關系看出:
          ? DistepcherServlet ---> FrameworkServlet ---> HttpServletBean---> HttpServlet
          ? 那就說明DistepcherServlet 類也是一個Servlet類,那最終核心的方法就是service()方法,即Servlet的核心方法。
          ? 那就找service()方法,在DistepcherServlet中沒有servic()方法,在父類FrameworkServlet有service()方法,源碼如下:
          來源:

          org.springframework.web.servlet.FrameworkServlet.service(HttpServletRequest request, HttpServletResponse response)

          /**
          	 * Override the parent class implementation in order to intercept PATCH requests.
          	 */
          	@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 {
          			super.service(request, response);
          		}
          	}
          

          可以看到:
          FrameworkServlet.service(HttpServletRequest request, HttpServletResponse response)拿到request請求,判斷當前請求是否是PATCH請求,不是的就調用父類的servic()方法,調用父類中的service方法就是去調用該類中doPost(),doGet()方法,根據不同的請求方式然后走doPost()或者doGet(),調用中以doGet()為例,
          FrameworkServlet類的doGet()源碼:

          /**
          	 * Delegate GET requests to processRequest/doService.
          	 * <p>Will also be invoked by HttpServlet's default implementation of {@code doHead},
          	 * with a {@code NoBodyResponse} that just captures the content length.
          	 * @see #doService
          	 * @see #doHead
          	 */
          	@Override
          	protected final void doGet(HttpServletRequest request, HttpServletResponse response)
          			throws ServletException, IOException {
          		processRequest(request, response);
          	}
          

          ? doGet()又調用FrameworkServlet類中的processRequest(request, response);

          /**
          	 * Process this request, publishing an event regardless of the outcome.
          	 * <p>The actual event handling is performed by the abstract
          	 * {@link #doService} template method.
          	 */
          	protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
          			throws ServletException, IOException {
          
          		long startTime=System.currentTimeMillis();
          		Throwable failureCause=null;
          
          		LocaleContext previousLocaleContext=LocaleContextHolder.getLocaleContext();
          		LocaleContext localeContext=buildLocaleContext(request);
          
          		RequestAttributes previousAttributes=RequestContextHolder.getRequestAttributes();
          		ServletRequestAttributes requestAttributes=buildRequestAttributes(request, response, previousAttributes);
          
          		WebAsyncManager asyncManager=WebAsyncUtils.getAsyncManager(request);
          		asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
          
          		initContextHolders(request, localeContext, requestAttributes);
          
          		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(request, response)方法中最關鍵的又調用了doService(request, response);查看FrameworkServlet類中的doService(request, response),或者是調試跟蹤可知,doService(request, response)由子類DispatcherServlet實現。

          源碼來源:

          org.springframework.web.servlet.FrameworkServlet.doService(HttpServletRequest request, HttpServletResponse response)

          /**
          	 * Subclasses must implement this method to do the work of request handling,
          	 * receiving a centralized callback for GET, POST, PUT and DELETE.
          	 * <p>The contract is essentially the same as that for the commonly overridden
          	 * {@code doGet} or {@code doPost} methods of HttpServlet.
          	 * <p>This class intercepts calls to ensure that exception handling and
          	 * event publication takes place.
          	 * @param request current HTTP request
          	 * @param response current HTTP response
          	 * @throws Exception in case of any kind of processing failure
          	 * @see javax.servlet.http.HttpServlet#doGet
          	 * @see javax.servlet.http.HttpServlet#doPost
          	 */
          	protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
          			throws Exception;
          

          ? 查看DispatcherServlet中的doService(HttpServletRequest request, HttpServletResponse response)方法

          /**
          	 * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch}
          	 * for the actual dispatching.
          	 */
          	@Override
          	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
          		logRequest(request);
          
          		// Keep a snapshot of the request attributes in case of an include,
          		// to be able to restore the original attributes after the include.
          		Map<String, Object> attributesSnapshot=null;
          		if (WebUtils.isIncludeRequest(request)) {
          			attributesSnapshot=new HashMap<>();
          			Enumeration<?> attrNames=request.getAttributeNames();
          			while (attrNames.hasMoreElements()) {
          				String attrName=(String) attrNames.nextElement();
          				if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
          					attributesSnapshot.put(attrName, request.getAttribute(attrName));
          				}
          			}
          		}
          
          		// Make framework objects available to handlers and view objects.
          		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) {
          			FlashMap inputFlashMap=this.flashMapManager.retrieveAndUpdate(request, response);
          			if (inputFlashMap !=null) {
          				request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
          			}
          			request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
          			request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
          		}
          
          		try {
          			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);
          				}
          			}
          		}
          	}
          

          ? DispatcherServlet的doService()方法中最終調用doDispatch(request, response),查看源碼如下:
          org.springframework.web.servlet.DispatcherServlet.doDispatch()

          /**
          	 * Process the actual dispatching to the handler.
          	 * <p>The handler will be obtained by applying the servlet's HandlerMappings in order.
          	 * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters
          	 * to find the first that supports the handler class.
          	 * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers
          	 * themselves to decide which methods are acceptable.
          	 * @param request current HTTP request
          	 * @param response current HTTP response
          	 * @throws Exception in case of any kind of processing failure
          	 */
          	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);
          				// 取得處理當前請求的controller,這里也稱為hanlder處理器,第一個步驟的意義就在這里體現了.這里并不是直接返回controller,而是返回的HandlerExecutionChain請求處理器鏈對象,該對象封裝了handler和攔截器interceptors.
          				// Determine handler for the current request.
          				mappedHandler=getHandler(processedRequest);
          				// 如果handler為空,則返回404
          				if (mappedHandler==null) {
          					noHandlerFound(processedRequest, response);
          					return;
          				}
          				//3. 獲取處理request的處理器適配器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 (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
          						return;
          					}
          				}
          				//處理器適配器執行之前,檢查攔截器的方法
          				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
          					return;
          				}
          				//處理器適配器根據找到,執行handler,返回ModelAndView
          				// 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);
          				}
          			}
          		}
          	}
          

          ? 可以看出doDispatch()就是SpringMVC的核心代碼了,分析doDispatch():

          2.1、查找處理器映射器HandlerMapping

          ? 首先看下處理器映射器HandlerMapping類圖:

          doDispatch()關鍵代碼:

          HandlerExecutionChain mappedHandler=null;
          
          mappedHandler=getHandler(processedRequest);
          

          ? mappedHandler是一個執行鏈HandlerExecutionChain 對象,這里封裝了handler和攔截器interceptors,getHandler(processedRequest)方法就是從處理器映射器HandlerMapping中找到url和controller的對應關系,并返回給前端控制器DispatchServlet。
          查看getHandler(processedRequest);源碼:

          /**
          	 * Return the HandlerExecutionChain for this request.
          	 * <p>Tries all handler mappings in order.
          	 * @param request current HTTP request
          	 * @return the HandlerExecutionChain, or {@code null} if no handler could be found
          	 */
          	@Nullable
          	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;
          	}
          

          調試代碼如下:


          從代碼調試中可以看到handlerMapping中有三個對象:

          this.handlerMappings={ArrayList@4662}  size=3
           0={BeanNameUrlHandlerMapping@4791} 
           1={RequestMappingHandlerMapping@4792} 
           2={RouterFunctionMapping@4793} 
          
          • BeanNameUrlHandlerMapping:初始化時會將urlpath做映射存儲(xml);
          • RequestMappingHandlerMapping:初始化時會將Controller中配置@RequestMapping注解的方法做映射存儲(注解);
          • RouterFunctionMapping:
            (這個對象不是太理解)
            這也就是為什么要去HandlerMapping找一個Handler了,因為處理器映射器HandlerMapping有不同的實現:
          • 1、xml方式
          • 2、注解方式

          接著看getHandler(HttpServletRequest request)方法,先遍歷HandlerMappers,查找控制器找到之后就返回執行鏈HandlerExecutionChain類型的Handler。

          可以看到返回的Handler中,拿到的就是我們自己編碼的Controller類,以及攔截器(演示項目中未編寫,所以調試匯總返回的Handler最后是0 interceptors)
          HandlerExecutionChain with [com.bjpowernode.controller.MyController#doSome()] and 0 interceptors


          將正在調試的idea打開自己編寫的Controller來對照,發現一致:

          2.2、根據處理器映射器HandlerMapping返回結果調用處理器適配器HandlerAdapter

          doDispatch()里面的關鍵代碼:

          HandlerAdapter ha=getHandlerAdapter(mappedHandler.getHandler());
          

          源碼如下:

          /**
          	 * Return the HandlerAdapter for this handler object.
          	 * @param handler the handler object to find an adapter for
          	 * @throws ServletException if no HandlerAdapter can be found for the handler. This is a fatal error.
          	 */
          	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
          		if (this.handlerAdapters !=null) {
          			for (HandlerAdapter adapter : this.handlerAdapters) {
          				if (adapter.supports(handler)) {
          					return adapter;
          				}
          			}
          		}
          		throw new ServletException("No adapter for handler [" + handler +
          				"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
          	}
          

          為什么還要獲取處理器適配器HandlerAdapter:與獲取處理器映射器HandlerMapping一樣,Spring提供了不通的處理器適配器。
          調試如下:


          查看DEBUG調試模式中getHandlerAdapter()方法在中的:
          handler、adapter、this.handlerAdapters


          以下是拷貝的結果:
          handler

          handler={HandlerMethod@4792} "com.bjpowernode.controller.MyController#doSome()"
           logger={LogAdapter$JavaUtilLog@4858} 
           bean={MyController@4859} 
           beanFactory={DefaultListableBeanFactory@4847} "org.springframework.beans.factory.support.DefaultListableBeanFactory@56b5a4c3: defining beans [myController,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,org.springframework.web.servlet.view.InternalResourceViewResolver#0]; root of factory hierarchy"
           beanType={Class@3782} "class com.bjpowernode.controller.MyController"
           method={Method@4860} "public org.springframework.web.servlet.ModelAndView com.bjpowernode.controller.MyController.doSome()"
           bridgedMethod={Method@4860} "public org.springframework.web.servlet.ModelAndView com.bjpowernode.controller.MyController.doSome()"
           parameters={MethodParameter[0]@4861} 
           responseStatus=null
           responseStatusReason=null
           resolvedFromHandlerMethod={HandlerMethod@4863} "com.bjpowernode.controller.MyController#doSome()"
           interfaceParameterAnnotations=null
           description="com.bjpowernode.controller.MyController#doSome()"
          

          adapter

          adapter={RequestMappingHandlerAdapter@4827} 
           customArgumentResolvers=null
           argumentResolvers={HandlerMethodArgumentResolverComposite@4833} 
           initBinderArgumentResolvers={HandlerMethodArgumentResolverComposite@4834} 
           customReturnValueHandlers=null
           returnValueHandlers={HandlerMethodReturnValueHandlerComposite@4835} 
           modelAndViewResolvers=null
           contentNegotiationManager={ContentNegotiationManager@4836} 
           messageConverters={ArrayList@4837}  size=4
           requestResponseBodyAdvice={ArrayList@4838}  size=0
           webBindingInitializer=null
           taskExecutor={SimpleAsyncTaskExecutor@4839} 
           asyncRequestTimeout=null
           callableInterceptors={CallableProcessingInterceptor[0]@4840} 
           deferredResultInterceptors={DeferredResultProcessingInterceptor[0]@4842} 
           reactiveAdapterRegistry={ReactiveAdapterRegistry@4844} 
           ignoreDefaultModelOnRedirect=false
           cacheSecondsForSessionAttributeHandlers=0
           synchronizeOnSession=false
           sessionAttributeStore={DefaultSessionAttributeStore@4845} 
           parameterNameDiscoverer={DefaultParameterNameDiscoverer@4846} 
           beanFactory={DefaultListableBeanFactory@4847} "org.springframework.beans.factory.support.DefaultListableBeanFactory@56b5a4c3: defining beans [myController,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,org.springframework.web.servlet.view.InternalResourceViewResolver#0]; root of factory hierarchy"
           sessionAttributesHandlerCache={ConcurrentHashMap@4848}  size=0
           initBinderCache={ConcurrentHashMap@4849}  size=0
           initBinderAdviceCache={LinkedHashMap@4850}  size=0
           modelAttributeCache={ConcurrentHashMap@4851}  size=0
           modelAttributeAdviceCache={LinkedHashMap@4852}  size=0
           order=2147483647
           supportedMethods=null
           allowHeader="GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS"
           requireSession=false
           cacheControl=null
           cacheSeconds=-1
           varyByRequestHeaders=null
           useExpiresHeader=false
           useCacheControlHeader=true
           useCacheControlNoStore=true
           alwaysMustRevalidate=false
           servletContext={ApplicationContextFacade@4754} 
           logger={LogAdapter$JavaUtilLog@4854} 
           applicationContext={XmlWebApplicationContext@4665} "WebApplicationContext for namespace 'myweb-servlet', started on Tue Mar 02 23:25:35 CST 2021"
           messageSourceAccessor={MessageSourceAccessor@4855} 
          

          this.handlerAdapters

          this.handlerAdapters={ArrayList@4658}  size=4
           0={HttpRequestHandlerAdapter@4810} 
           1={SimpleControllerHandlerAdapter@4820} //XML方式
           2={RequestMappingHandlerAdapter@4827} //注解方式
           3={HandlerFunctionAdapter@4832} 
          

          可以看到找到4個處理器適配器。通過DEBUG模式可以看到,此次取到的處理器適配器HandlerAdapter是:RequestMappingHandlerAdapter

          ha={RequestMappingHandlerAdapter@4827} 
          

          2.3、檢查攔截器Interceptor

          doDispatch()中的關鍵代碼:

          if (!mappedHandler.applyPreHandle(processedRequest, response)) {
          					return;
          				}
          org.springframework.web.servlet.HandlerExecutionChain#applyPreHandle
          

          applyPreHandle(processedRequest, response)源碼:

          /**
          	 * Apply preHandle methods of registered interceptors.
          	 * @return {@code true} if the execution chain should proceed with the
          	 * next interceptor or the handler itself. Else, DispatcherServlet assumes
          	 * that this interceptor has already dealt with the response itself.
          	 */
          	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
          		HandlerInterceptor[] interceptors=getInterceptors();
          		if (!ObjectUtils.isEmpty(interceptors)) {
          			for (int i=0; i < interceptors.length; i++) {
          				HandlerInterceptor interceptor=interceptors[i];
          				if (!interceptor.preHandle(request, response, this.handler)) {
          					triggerAfterCompletion(request, response, null);
          					return false;
          				}
          				this.interceptorIndex=i;
          			}
          		}
          		return true;
          	}
          

          2.3、處理器適配器HandlerAdapter執行Handler(Controller)返回ModelAndView

          doDispatch()中的關鍵代碼:

          mv=ha.handle(processedRequest, response, mappedHandler.getHandler());
          

          DEBUG模式調試,是調到了:
          org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter#handle
          源碼如下:

          /**
          	 * This implementation expects the handler to be an {@link HandlerMethod}.
          	 */
          	@Override
          	@Nullable
          	public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
          			throws Exception {
          
          		return handleInternal(request, response, (HandlerMethod) handler);
          	}
          

          再往下看handleInternal(request, response, (HandlerMethod) handler)方法,
          org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#handleInternal

          @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 necessary
          				mav=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;
          	}
          

          注意,handleInternal(request, response, (HandlerMethod) handler)方法的返回值是ModelAndView ,這里就完成了處理器適配器HandlerAdapter執行Handler(Controller)并將結果ModelAndView返回給前端控制器DistepchServlet

          2.4、視圖解析器ViewResolver

          ??接上2.3:前端控制器DistepchServlet接收到處理器適配器HandlerAdapter返回的ModelAndView以后,這里分2種情況:

          • (1)、如果ModelAndView里面是邏輯視圖
            前端控制器DistepchServlet調用視圖解析器ViewResolver通過邏輯視圖查找真正的視圖對象View,并返回給前端控制器DistepchServlet。
          • (2)、如果ModelAndView里面是非邏輯視圖:
            如:MappingJackson2JsonView(把當前數據轉為為JSON數據,并不需要對視圖邏輯名稱進行轉換)

          總結一下:
          視圖解析器ViewResolver接口主要作用是解析前端控制器DispatcherServlet傳遞的邏輯視圖名,并將解析結果的真正的視圖對象View傳回給前端控制器DispatcherServlet

          ViewResolverd的實現類:


          ViewResolver的UML:

          2.5、視圖View

          2.5.1、視圖對象的作用

          • (1)、將控制器返回的數據處理渲染,最終返回客戶端展示給用戶,主要就是完成轉發或者是重定向的操作.。
          • (2)、為了實現視圖模型和具體實現技術的解耦(指的是Spring在org.springframework.web.servlet包中定義的抽象View接口),詳見2.5.2View接口圖。
          • (3)、視圖對象View由視圖解析器負責實例化。由于視圖是無狀態(每一次請求都會創建一個新的view對象)的,所以不會有線程安全的問題.

          2.5.2、View接口圖

          2.5.3、View的實現類圖

          2.5.4、View的UML圖

          ![2021022601-20-01-View-uml(hierarchic group layout)](https://gitee.com/chuchq/blogs-gallery/raw/master/images / 2021/2021022601-20-01-View-uml(hierarchic group layout).png)

          2.5.5、常用的View視圖類

          視圖類型

          簡介

          URL視圖資源圖

          InternalResourceView

          將JSP或其他資源封裝成一個視圖。被視圖解析器InternalResourceViewResolver默認使用。

          JstlView

          InternalResourceView的子類。如果JSP中使用了JSTL的國際化標簽,就需要使用該視圖類。

          文檔視圖

          AbstractExcelView

          Excel文檔視圖的抽象類。

          AbstractPdfView

          PDF文檔視圖的抽象類

          報表視圖

          ConfigurableJasperReportsView

          常用的JasperReports報表視圖

          JasperReportsHtmlView

          JasperReportsPdfView

          JasperReportsXlsView

          JSON視圖

          MappingJackson2JsonView

          將數據通過Jackson框架的ObjectMapper對象,以JSON方式輸出

          2.6、其他重要的點

          2.6.1、DispatcherServlet.properties

          DispatcherServlet.properties文件是在SpringMVC架包中:


          DispatcherServlet.properties內容:

          # Default implementation classes for DispatcherServlet's strategy interfaces.
          # Used as fallback when no matching beans are found in the DispatcherServlet context.
          # Not meant to be customized by application developers.
          
          org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
          
          org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
          
          org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
          	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\
          	org.springframework.web.servlet.function.support.RouterFunctionMapping
          
          org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
          	org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
          	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
          	org.springframework.web.servlet.function.support.HandlerFunctionAdapter
          
          
          org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
          	org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
          	org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
          
          org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
          
          org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
          
          org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
          

          SpringMVC為什么能加載不同處理器映射器HandlerMapping、處理器適配器handlerAdapter,就是因為框架配置了這個DispatcherServlet.properties文件。

          轉載于:https://www.cnblogs.com/chuchq/p/14489716.html

          VC作為WEB程序的通用架構深受廣大WEB程序員的擁護。 更是廣泛成為面試題、考試試題、答辯問題等題材進行使用。

          目前主流的語言框架及WEB系統大部分都是以MVC做為基礎架構進行編寫的。對于初學者而言理解MVC架構與熟悉業務邏輯的編寫能極大的縮短學習一門語言框架的時間。然而初學者僅通過教材或許很難真正的理解MVC的架構及用法,在學習的過程中也可通過分析理解一些經典的語言框架進行學習。(例如:JSP + servlet + javabean)

          初步認知

          MVC模型:作為一種架構型的模式,無法直接實現功能添加,其主要作用是幫助我們對開發結構進行分層,使得代碼結構更佳清晰合理化,將模型(model)、視圖(view)、控制器(controller)進行分離。

          將MVC通俗的理解可以想象成建筑一棟高樓大廈:

          • MVC:最終項目展示結果

          • 模型(Model):建筑材料、建筑工具的集合(WEB項目中的數據模型及函數方法模型)

          • 視圖(View):建筑設計圖紙、建筑展示效果圖(WEB項目中數據的展示形態,優化豐富控制器傳遞過來的數據并進行展示)

          • 控制器(Controller):建筑工程師、施工人員(WEB項目中通過調取對應數據模型及函數方法模型產生數據并傳遞給相關視圖層)

          施工人員(Controller)通過使用建筑工具(Model)將建筑材料(Model)按著設計圖紙(View)進行搭建填裝,最后組裝成一棟高樓大廈。

          深入理解

          模型(Model):模型層的重點在于模塊化,重點思路在于將可復用可集成的代碼封裝成函數方法集成在模型層中方便復用及代碼維護。(例如:用戶登錄驗證,這段代碼可能在多個不同的控制器中都需要使用,若將其封裝成方法寫入模型層,則控制器如果有需求只需導入對應的模型層并以傳參的方式進行調用既可省去大量重復代碼,并且在進行維護的時候也僅需對模型層中的封裝方法進行修改就能實現全局維護)

          視圖(View):視圖層的重點在于數據展示,視圖層的框架要便于數據展示,避免大量邏輯代碼的存在,盡量通過簡單的循環語句將控制層獲取的數據進行展示,所以的數據處理應盡量于控制層與模型層實現。(當然視圖層也可以適當使用模塊化的設計思路,例如:標準WEB網站中,大量網頁頭部及尾部代碼都是重復的,這些重復視圖層代碼可以通過MVC模式的思路封裝到一個單獨的HTML頁面,在需要時通過相關語句載入對應的頁面中,可以大量減少重復代碼。)

          控制器(Controller):控制層主要做為模型層與視圖層的控制中樞。控制器應避免重復視圖層與模型層的工作,將數據的展示封裝至模型層,可復用的數據獲取與數據邏輯處理則在模型層中封裝成函數方法。控制層應做到精簡。

          學編程技術,就到W3Cschool,如果你喜歡我們的文章,可以點擊右上角關注我們;如果你想看到更多IT界的資訊,可以加我們的公眾號。

          公眾號:w3cschoolcn


          主站蜘蛛池模板: 亚洲午夜精品第一区二区8050| 蜜臀AV免费一区二区三区| 99国产精品欧美一区二区三区| 在线日韩麻豆一区| 久久久无码精品国产一区| 国产一区二区精品久久91| 亚洲一区二区三区亚瑟| 国产精品无码一区二区在线观一| 人妻激情偷乱视频一区二区三区| 中文字幕在线播放一区| 八戒久久精品一区二区三区| 濑亚美莉在线视频一区| 99精品一区二区三区无码吞精| 丰满爆乳一区二区三区| 制服中文字幕一区二区| 国产一区二区三区在线| 制服美女视频一区| 美女免费视频一区二区三区| 亚洲午夜在线一区| 色噜噜狠狠一区二区| 2018高清国产一区二区三区| 亚洲日韩AV一区二区三区中文| 亚洲国产一区在线观看| 2021国产精品视频一区| 亚洲精品日韩一区二区小说| 一区二区三区在线观看| 在线精品一区二区三区电影 | 国产一区高清视频| 九九无码人妻一区二区三区| 亚洲av乱码中文一区二区三区 | 3d动漫精品一区视频在线观看| 日韩一区二区电影| 亚洲区精品久久一区二区三区| 秋霞午夜一区二区| 国产精品久久久久久麻豆一区| 无码少妇丰满熟妇一区二区| 立川理惠在线播放一区| 久久成人国产精品一区二区| 日韩精品在线一区二区| 无码福利一区二区三区| 日本免费一区二区三区最新|