詳解springMVC容器加載源碼分析
springmvc是一個(gè)基于servlet容器的輕量靈活的mvc框架,在它整個(gè)請(qǐng)求過程中,為了能夠靈活定制各種需求,所以提供了一系列的組件完成整個(gè)請(qǐng)求的映射,響應(yīng)等等處理。這里我們來分析下springMVC的源碼。
首先,spring提供了一個(gè)處理所有請(qǐng)求的servlet,這個(gè)servlet實(shí)現(xiàn)了servlet的接口,就是DispatcherServlet。把它配置在web.xml中,并且處理我們?cè)谡麄€(gè)mvc中需要處理的請(qǐng)求,一般如下配置:
<servlet> <servlet-name>spring-servlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring-servlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
DispatcherServlet也繼承了FrameworkServlet抽象類,這個(gè)抽象類為整個(gè)servlet的處理提供了spring容器的支持,讓原本servlet容器的DispatcherServlet擁有能夠利用spring容器組件的能力。上面servlet的初始化參數(shù)contextConfigLocation就是DispatcherServlet獲取spring容器的配置環(huán)境。FrameworkServlet繼承自org.springframework.web.servlet.HttpServletBean。HttpServletBean實(shí)現(xiàn)了servlet容器初始化會(huì)調(diào)用的init函數(shù)。這個(gè)init函數(shù)會(huì)調(diào)用FrameworkServlet的初始化加載spring容器方法。方法源碼:
@Override protected final void initServletBean() throws ServletException { getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'"); if (this.logger.isInfoEnabled()) { this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started"); } long startTime = System.currentTimeMillis(); try { // 初始化spring-servlet容器 this.webApplicationContext = initWebApplicationContext(); initFrameworkServlet(); } catch (ServletException ex) { this.logger.error("Context initialization failed", ex); throw ex; } catch (RuntimeException ex) { this.logger.error("Context initialization failed", ex); throw ex; } if (this.logger.isInfoEnabled()) { long elapsedTime = System.currentTimeMillis() - startTime; this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " + elapsedTime + " ms"); } }
可以看到這里就觸發(fā)了spring的對(duì)web支持的容器初始化,這里使用的容器為WebApplicationContext.接下來我們就來分析一下整個(gè)容器的初始化過程:
protected WebApplicationContext initWebApplicationContext() { // 查看是否在servlet上下文有所有容器需要繼承的根Web容器 WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null; // 如果容器已經(jīng)加載 if (this.webApplicationContext != null) { // A context instance was injected at construction time -> use it wac = this.webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { // The context has not yet been refreshed -> provide services such as // setting the parent context, setting the application context id, etc if (cwac.getParent() == null) { // The context instance was injected without an explicit parent -> set // the root application context (if any; may be null) as the parent cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null) { // No context instance was injected at construction time -> see if one // has been registered in the servlet context. If one exists, it is assumed // that the parent context (if any) has already been set and that the // user has performed any initialization such as setting the context id wac = findWebApplicationContext(); } if (wac == null) { // 創(chuàng)建容器 wac = createWebApplicationContext(rootContext); } if (!this.refreshEventReceived) { // Either the context is not a ConfigurableApplicationContext with refresh // support or the context injected at construction time had already been // refreshed -> trigger initial onRefresh manually here. onRefresh(wac); } if (this.publishContext) { // Publish the context as a servlet context attribute. String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); if (this.logger.isDebugEnabled()) { this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() + "' as ServletContext attribute with name [" + attrName + "]"); } } return wac; }
如果已經(jīng)有容器被創(chuàng)建那就初始化。否則創(chuàng)建容器,創(chuàng)建邏輯:
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) { Class<?> contextClass = getContextClass(); if (this.logger.isDebugEnabled()) { this.logger.debug("Servlet with name '" + getServletName() + "' will try to create custom WebApplicationContext context of class '" + contextClass.getName() + "'" + ", using parent context [" + parent + "]"); } // 判斷是否是可配置的容器 if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext"); } // 如果找到目標(biāo)容器,并且可配置,然后就開始獲取配置文件并開始初始化 ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); wac.setConfigLocation(getContextConfigLocation()); // 這里是獲取配置文件和完成初始化web容器的入口 configureAndRefreshWebApplicationContext(wac); return wac; }
這里完成了 web容器的相關(guān)準(zhǔn)備工作,開始正式讀取配置文件加載和初始化容器。
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) { // 給容器設(shè)置一個(gè)id if (ObjectUtils.identityToString(wac).equals(wac.getId())) { // The application context id is still set to its original default value // -> assign a more useful id based on available information if (this.contextId != null) { wac.setId(this.contextId); } else { // Generate default id... wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName()); } } wac.setServletContext(getServletContext()); wac.setServletConfig(getServletConfig()); wac.setNamespace(getNamespace()); wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener())); // The wac environment's #initPropertySources will be called in any case when the context // is refreshed; do it eagerly here to ensure servlet property sources are in place for // use in any post-processing or initialization that occurs below prior to #refresh ConfigurableEnvironment env = wac.getEnvironment(); if (env instanceof ConfigurableWebEnvironment) { ((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig()); } /* 這里在容器被激活后, 并且容器還沒完成初始化之前可以對(duì)容器的相關(guān)配置做一些修改, 默認(rèn)給了空實(shí)現(xiàn), 這里子類可以去重寫,能夠獲得在容器初始化之前做 一些處理*/ postProcessWebApplicationContext(wac); // 這里講容器的初始化信息放到一個(gè)列表 applyInitializers(wac); // 這里就開始web容器的初始化 wac.refresh(); }
容器的初始化在AbstractApplicationContext,無論是其他的容器,最終都會(huì)調(diào)用到refresh()函數(shù),這個(gè)函數(shù)基本定義了整個(gè)容器初始化的整個(gè)脈絡(luò),這里不展開講,本博客之后應(yīng)該會(huì)詳細(xì)分析這塊的邏輯,這里大概的注釋一下每一個(gè)函數(shù)完成的操作:
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 這里主要加載了容器當(dāng)中一些從其他配置文件讀取的變量 prepareRefresh(); // 獲取容器本身 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 這里完成一些基礎(chǔ)組件的依賴 prepareBeanFactory(beanFactory); try { // 添加 容器初始化之前的前置處理 postProcessBeanFactory(beanFactory); // 調(diào)用 前置處理器,其中包含invokeBeanDefinitionRegistryPostProcessors與invokeBeanFactoryPostProcessors兩類前置處理器的調(diào)用 invokeBeanFactoryPostProcessors(beanFactory); // 注冊(cè)bean被創(chuàng)建之前的前置處理器 registerBeanPostProcessors(beanFactory); // 初始化容器的編碼源 initMessageSource(); // 初始化一些事件監(jiān)聽器 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // 注冊(cè)容器監(jiān)聽器 registerListeners(); // 初始化所有非懶加載的beans finishBeanFactoryInitialization(beanFactory); // Last step: 事件通知關(guān)心容器加載的相關(guān)組件 finishRefresh(); } // 部分代碼省略 } } }
自此加載完畢核心容器,然后回到FramewordServlet的initWebApplicationContext函數(shù),在調(diào)用createWebApplicationContext完成一系列上面的操作之后,需要mvc servlet組件,入口就是onRefresh(ApplocationContext context)方法。它會(huì)調(diào)用另一個(gè)方法initStrategies(ApplicationContext context)。該方法如下:
protected void initStrategies(ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); // 獲取所有的RequestMappings initHandlerMappings(context); // 不同handler的適配器 initHandlerAdapters(context); // 異常解析器 initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
這里我們重點(diǎn)講解initHandlerMappings與initHandlerAdapters函數(shù),因?yàn)檫@兩個(gè)是處理servlet請(qǐng)求的入口。
在spring mvc中任何類都可以處理request請(qǐng)求,因?yàn)镈ispacherServlet也是實(shí)現(xiàn)了HttpServlet的接口,所以處理請(qǐng)求也是doService里。doService會(huì)將請(qǐng)求交給doDispatcher函數(shù)處理。然后doDispacher的源碼:
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); // 這里獲取當(dāng)前請(qǐng)求的處理handler mappedHandler = getHandler(processedRequest, false); if (mappedHandler == null || mappedHandler.getHandler() == null) { noHandlerFound(processedRequest, response); return; } /* 因?yàn)閔andler可以是任何類, 但是我們的DispacherServlet需要一個(gè)統(tǒng)一的處理接口,這個(gè)接口就是HandlerAdapter, 不同的HandlerMapping可以獲取到各種各樣的Handler, 這個(gè)handler最后都必須得到HandlerAdapter的支持才能被DispacherServlet所調(diào)用。 擴(kuò)充一個(gè)新的HandlerMapping只需要添加一個(gè)新的HandlerAdatper即可,有點(diǎn)抽象工廠模式的味道*/ 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; } // Actually invoke the handler. mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } applyDefaultViewName(request, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Error err) { triggerAfterCompletionWithError(processedRequest, response, mappedHandler, 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); } } } }
但是我們發(fā)現(xiàn)獲取到的handler并不是Object而是一個(gè)HandlerExecutionChain,這個(gè)類可以進(jìn)去查看發(fā)現(xiàn)是一堆攔截器和一個(gè)handler,主要就是給每一個(gè)請(qǐng)求一個(gè)前置處理的機(jī)會(huì),這里值得一提的是一般來說攔截器和過濾器的區(qū)別就是攔截器可以終止后續(xù)執(zhí)行流程,而過濾器一般不終止。過濾器一般是容器級(jí)別的,這個(gè)handler前置攔截器可以做到更細(xì)級(jí)別的控制,例如過濾器只定義了init和doFilter,但是這個(gè)handler攔截器定義了preHandle和postHandle還有afterCompletion函數(shù),不難理解分別對(duì)應(yīng)不同請(qǐng)求階段的攔截粒度,更加靈活。
獲取處理handler的getHandler代碼:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 這里獲取多個(gè)映射方式,一旦找到合適的處理請(qǐng)求的handler即返回 for (HandlerMapping hm : this.handlerMappings) { if (logger.isTraceEnabled()) { logger.trace( "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'"); } // HandlerExecutionChain包含了 HandlerExecutionChain handler = hm.getHandler(request); if (handler != null) { return handler; } } return null; }
HandlerAdapter處理后返回統(tǒng)一被封裝成ModelAndView對(duì)象,這個(gè)就是包含試圖和數(shù)據(jù)的對(duì)象,在經(jīng)過適當(dāng)?shù)奶幚恚?/p>
processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
將頁面與返回的數(shù)據(jù)返回給瀏覽器完成整個(gè)請(qǐng)求過程。以上就是springMVC大概的啟動(dòng)過程和請(qǐng)求的處理過程,之后本博客還會(huì)陸續(xù)分析核心的spring源碼,博主一直認(rèn)為精讀著名框架源碼是在短時(shí)間提升代碼能力的捷徑,因?yàn)檫@些輪子包含太多設(shè)計(jì)思想和細(xì)小的代碼規(guī)范,這些讀多了都會(huì)潛移默化的形成一種本能的東西。設(shè)計(jì)模式也不斷貫穿其中。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java雜談之代碼重構(gòu)的方法多長(zhǎng)才算長(zhǎng)
關(guān)于代碼重構(gòu)的理解:在不改變軟件系統(tǒng)/模塊所具備的功能特性的前提下,遵循/利用某種規(guī)則,使其內(nèi)部結(jié)構(gòu)趨于完善。其在軟件生命周期中的價(jià)值體現(xiàn)主要在于可維護(hù)性和可擴(kuò)展性2021-10-10Maven高級(jí)的聚合和繼承的實(shí)現(xiàn)
在軟件開發(fā)中,隨著項(xiàng)目規(guī)模的擴(kuò)大,單個(gè)模塊的開發(fā)方式逐漸轉(zhuǎn)變?yōu)槎嗄K開發(fā),這種方式帶來了項(xiàng)目管理上的挑戰(zhàn),其中最常見的問題是模塊間的依賴管理和版本控制問題,本文就來介紹一下2024-10-10MyBatis-Plus 如何單元測(cè)試的實(shí)現(xiàn)
這篇文章主要介紹了MyBatis-Plus 如何單元測(cè)試的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08詳解SpringBoot中Controller接收對(duì)象列表實(shí)現(xiàn)
這篇文章主要介紹了詳解SpringBoot中Controller接收對(duì)象列表實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過程
這篇文章主要介紹了基于IDEA 的遠(yuǎn)程調(diào)試 Weblogic的操作過程,本文通過圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09MyBatis insert操作插入數(shù)據(jù)之后返回插入記錄的id
今天小編就為大家分享一篇關(guān)于MyBatis插入數(shù)據(jù)之后返回插入記錄的id,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2019-03-03Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享
這篇文章主要介紹了Java面試題-實(shí)現(xiàn)復(fù)雜鏈表的復(fù)制代碼分享,小編覺得還是挺不錯(cuò)的,具有參考價(jià)值,需要的朋友可以了解下。2017-10-10