前言
學了一遍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、ApplicationContext2、前端控制器(中央處理器)DistepcherServlet2.1、查詢處理器對映器HandlerMapping2.2、根據處理器對映器HandlerMapping返回結果呼叫處理器介面卡HandlerAdapter2.3、檢查攔截器Interceptor2.3、處理器介面卡HandlerAdapter執行Handler(Controller)返回ModelAndView2.4、檢視解析器ViewResolver2.5、檢視View2.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
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.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的詳細流程圖:
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返回結果呼叫處理器介面卡HandlerAdapterdoDispatch()裡面的關鍵程式碼:
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)返回ModelAndViewdoDispatch()中的關鍵程式碼:
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、檢視View2.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方式輸出 |
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.AcceptHeaderLocaleResolverorg.springframework.web.servlet.ThemeResolver= org.springframework.web.servlet.theme.FixedThemeResolverorg.springframework.web.servlet.HandlerMapping= org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\ org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping,\ org.springframework.web.servlet.function.support.RouterFunctionMappingorg.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.HandlerFunctionAdapterorg.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.DefaultHandlerExceptionResolverorg.springframework.web.servlet.RequestToViewNameTranslator= org.springframework.web.servlet.view.DefaultRequestToViewNameTranslatororg.springframework.web.servlet.ViewResolver= org.springframework.web.servlet.view.InternalResourceViewResolverorg.springframework.web.servlet.FlashMapManager= org.springframework.web.servlet.support.SessionFlashMapManager
SpringMVC為什麼能載入不同處理器對映器HandlerMapping、處理器介面卡handlerAdapter,就是因為框架配置了這個DispatcherServlet.properties檔案。